我认为最小割做这道题,朋友之间不用建双向边?
查看原帖
我认为最小割做这道题,朋友之间不用建双向边?
425074
ChevalierDeSangreal楼主2021/8/3 15:25
  我看有几篇题解里强调,一对朋友之间一定要建双向边。但是考虑到跑最大流的过程:从源点s到汇点t,边权都为1,怎么可能会经过这条反向边呢?因此没有必要建双向边,只要建从s到t方向的那一条边就行。

丑陋的图 结合图片使用更佳

  为了验证我的猜想,我写了一份如上文所述方式建边的代码,成功A掉了。不过考虑到数据比较水(据说),我不确定我的想法是否正确,望各位大佬指点一下。

代码如下

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#define inf 1 << 30
using namespace std;
int n, m, s, t;
int tot = 1, head[305], ver[400010], nxt[400010], edge[400010];
int d[305], now[305], side[305];
queue<int> q;

void add(int x, int y, int z){
    ver[++tot] = y;edge[tot] = z;
    nxt[tot] = head[x];head[x] = tot;
    ver[++tot] = x;edge[tot] = 0;
    nxt[tot] = head[y];head[y] = tot;
}

bool bfs(){
    memset(d, 0, sizeof d);
    while(q.size())q.pop();
    d[s] = 1;now[s] =  head[s];
    q.push(s);
    while(q.size()){
        int x = q.front();q.pop();
        for(int i=head[x];i;i=nxt[i]){
            int y = ver[i];
            int z = edge[i];
            if(z && !d[y]){
                q.push(y);
                now[y] = head[y];
                d[y] = d[x] + 1;
                if(y == t)return 1;
            }
        }
    }
    return 0;
}

int dinic(int x, int flow){
    if(x == t)return flow;
    int res = flow, k, i;
    for(i = now[x];i&&res;i=nxt[i]){
        int y = ver[i];
        int z = edge[i];
        if(z && d[y] == d[x] + 1){
            k = dinic(y, min(res, z));
            if(!k)d[y] = 0;
            edge[i] -= k;
            edge[i^1] += k;
            res -= k;
        }
    }
    now[x] = i;
    return flow - res;
}

int main(){
    scanf("%d%d", &n, &m);
    s = n + 1;t = n + 2;
    for(int i=1;i<=n;i++){
        scanf("%d", &side[i]);
        if(side[i])add(s, i, 1);
        else add(i, t, 1);
    }
    for(int i=1;i<=m;i++){
        int x, y;
        scanf("%d%d", &x, &y);
        if(side[x] == side[y])continue;
        if(side[x])add(x, y, 1);
        else add(y, x, 1);
    }
    int ans = 0, flow;
    while(bfs())
        while(flow = dinic(s, inf))
            ans += flow;
    printf("%d\n", ans);
    return 0;
}
  顺带一提,@ononesown大佬在题解中提到了“因为对称性所以建双向边”。一开始觉得很有道理。但是事后想起来发现不对:在分出“源点”和“汇点”的时候,对称性已经被破坏了。
  如果我的猜想正确的话,朋友之间建立双向边唯一的用处就是“不用判断边关于s和t的方向”。
2021/8/3 15:25
加载中...