最大流&最小割
真是不知道该说些什么呀……
感觉这是我见到过的网上叙述最最最详细的一个算法了。(可见我才学过几个算法qwq……)
我并不认为我能比网上讲的要好……
所以……emmm……
我就打算解释一下>\\\<(不要管这迷一样的逻辑)
(先去看看题解hhh)
咳咳……
(等会儿……)
好了好了(qwq)……
言归正传:
这里的dinic算法,是对Edmonds-Karp算法的一个优化 ,复杂度嘛……手推是O(n2 * m)。
对于要连反向边的原因,就是:
我们知道,当我们在寻找增广路的时候,在前面找出的不一定是最优解,如果我们在减去残量网络中正向边的同时将相对应的反向边加上对应的值,我们就相当于可以反悔从这条边流过。
比如说我们现在选择从u流向v一些流量,但是我们后面发现,如果有另外的流量从p流向v,而原来u流过来的流量可以从u->q流走,这样就可以增加总流量,其效果就相当于p->v->u->q。
通俗点说,就是:
反向边就相当于把用了的流量(减去多少)给退了回去(反向边就加多少),这样可以给程序一个后悔的机会,去选择一个更有的解。
对于反向边的存储:
我们可以发现:1^1=0;0^1=1;2^1=3;3^1=2;
所以如果我们从0开始存边的话,那么x^1便可以表示反向边。(所以head,nxt要改啊)……
接下来上代码:
#include<cstdio> #include<iostream> #include<queue> #include<cstring> using namespace std; const int maxn=1e6+5; const int inf=1e9; struct node { int nxt=-1,to,w; } edge[maxn]; int head[maxn],dep[maxn]; int n,m,s,t,cnt=-1; void add(int a,int b,int z) { edge[++cnt].to=b; edge[cnt].nxt=head[a]; head[a]=cnt; edge[cnt].w=z; } int bfs() { memset(dep,0,sizeof(dep)); queue<int> q; while(!q.empty()) q.pop(); dep[s]=1; q.push(s); while(!q.empty()) { int u=q.front(); q.pop(); for(int i=head[u];i!=-1;i=edge[i].nxt) { int to=edge[i].to; if(edge[i].w>0&&!dep[to]) { dep[to]=dep[u]+1; q.push(to); } } } if(!dep[t]) return 0; else return 1; } int dfs(int u,int dist) { if(u==t) return dist; for(int i=head[u];i!=-1;i=edge[i].nxt) { int to=edge[i].to; if(dep[to]==dep[u]+1&&edge[i].w) { int di=dfs(to,min(dist,edge[i].w)); if(di>0) { edge[i].w-=di; edge[i^1].w+=di; return di; } } } return 0; } int dinic() { int ans=0; while(bfs()) while(int di=dfs(s,inf)) ans+=di; return ans; } int main() { scanf("%d%d%d%d",&n,&m,&s,&t); memset(head,-1,sizeof(head)); for(int i=1;i<=m;i++) { int x,y,z; scanf("%d%d%d",&x,&y,&z); add(x,y,z); add(y,x,0); } printf("%d",dinic()); return 0; }

浙公网安备 33010602011771号