网络流问题可以看成是从一个单位时间流量超级大的水龙头经过一些粗细不一的水管流向下水道的问题,一般情况下是要求合理分配流量,使得单位时间内流向下水道的水量最大。
现在,水龙头开闸放水,假设自己是一个水分子。那么会怎么走呢?当然是找出一条能走到下水道的并且没有被流满的路,然后沿着这条路走到下水道就行了。网络流的增广路算法就是模拟这个过程,每次找出一条可以走到终点的路,把这条路流满,再找下一条路。注意不要忘记建反向弧,反向弧相当于反悔的机会。用水来比喻的话,就是一来一回两股水流,相当于没有,互相抵消了。
ek算法就是每次bfs找出可行的路,把这条路流满,再找下一条,复杂度是o(点数*(边数^2)) 。
dinic的原理和ek类似,但是它是每次一边bfs给图分层,然后dfs,只走dis[u]+1==dis[v]的边(保证不会原地绕圈),无路可走就再bfs一遍,直到原点走不到汇点,复杂度是o(边数*(点数^2),代码也不是很难写,所以大部分人都用这个。
#include<iostream> #include<iomanip> #include<cstdio> #include<cstdlib> #include<cstring> #include<cmath> #include<algorithm> #include<queue> #define maxn 10010 #define maxm 100010 using namespace std; int fl[maxm*2],v[maxm*2],w[maxm*2],nxt[maxm*2],fir[maxn]; int n,m,kk,cnt,dis[maxn],dep[maxn],inf,s,t,maxflow; bool vis[maxn]; int read() { int x=0,f=1; char ch=getchar(); while(isdigit(ch)==0 && ch!='-')ch=getchar(); if(ch=='-')f=-1,ch=getchar(); while(isdigit(ch))x=x*10+ch-'0',ch=getchar(); return x*f; } void addedge(int u1,int v1,int fl1) { fl[cnt]=fl1,v[cnt]=v1,nxt[cnt]=fir[u1],fir[u1]=cnt;cnt++; fl[cnt]=0,v[cnt]=u1,nxt[cnt]=fir[v1],fir[v1]=cnt;cnt++; } int bfs() { memset(dep,-1,sizeof(dep)); queue<int>q; dep[s]=0; q.push(s); while(!q.empty()) { int u=q.front();q.pop(); for(int k=fir[u];k!=-1;k=nxt[k]) { int vv=v[k]; if(fl[k]>0) { if(dep[vv]==-1 || dep[vv]>dep[u]+1) { dep[vv]=dep[u]+1; q.push(vv); } } } } return dep[t]; } int dfs(int u,int nowflow) { int flow=0,tmp; if(u==t || nowflow==0)return nowflow; for(int k=fir[u];k!=-1;k=nxt[k]) { int i=v[k]; if(fl[k]>0 && dep[i]==dep[u]+1 && (tmp=dfs(i,min(nowflow,fl[k])))>0) { flow+=tmp; fl[k]-=tmp; fl[k^1]+=tmp; nowflow-=tmp; } } return flow; } int main() { memset(fir,-1,sizeof(fir)); n=read(),m=read(),s=read(),t=read(); for(int i=1;i<=m;i++) { int u1=read(),v1=read(),fl1=read(); addedge(u1,v1,fl1); } while(bfs()!=-1) { maxflow+=dfs(s,0x7fffffff); } cout<<maxflow; return 0; }
事实上,某些神奇优化+卡常能使它跑的极快。
#include <bits/stdc++.h> #define maxn 1000010 #define maxm 8000010 #define LL long long #define re register using namespace std; struct maxflow { int v[maxm*2],nxt[maxm*2],fir[maxn],tfir[maxn]; int n,m,kk,cnt,dis[maxn],dep[maxn],inf[10],s,t,maxflow; LL fl[maxm*2]; inline LL llread() { LL x=0,f=1; char ch=getchar(); while(isdigit(ch)==0 && ch!='-')ch=getchar(); if(ch=='-')f=-1,ch=getchar(); while(isdigit(ch))x=x*10+ch-'0',ch=getchar(); return x*f; } inline int read() { int x=0,f=1; char ch=getchar(); while(isdigit(ch)==0 && ch!='-')ch=getchar(); if(ch=='-')f=-1,ch=getchar(); while(isdigit(ch))x=x*10+ch-'0',ch=getchar(); return x*f; } inline void write(int x) { int f=0;char ch[20]; if(!x){puts("0");return;} if(x<0){putchar('-');x=-x;} while(x)ch[++f]=x%10+'0',x/=10; while(f)putchar(ch[f--]); putchar(' '); } inline void addedge(int u1,int v1,LL fl1) { fl[cnt]=fl1,v[cnt]=v1,nxt[cnt]=fir[u1],fir[u1]=cnt;cnt++; fl[cnt]=0,v[cnt]=u1,nxt[cnt]=fir[v1],fir[v1]=cnt;cnt++; } bool bfs() { for(int i=1;i<=n;i++) dep[i]=-1; queue<int >q; dep[t]=0; q.push(t); while(!q.empty()) { int u=q.front();q.pop(); for(re int k=fir[u];k!=-1;k=nxt[k]) { int vv=v[k]; if(fl[k^1]>0) { if(dep[vv]==-1) { dep[vv]=dep[u]+1; if(vv==s)return 1; q.push(vv); } } } } return 0; } int dfs(int u,LL nowflow) { LL flow=0,tmp; if(u==t || !nowflow)return nowflow; for(re int &k=tfir[u];k!=-1;k=nxt[k]) { int i=v[k]; if(!fl[k])continue; if(dep[u]==dep[i]+1 && (tmp=dfs(i,min(nowflow,fl[k])))>0) { flow+=tmp; fl[k]-=tmp; fl[k^1]+=tmp; nowflow-=tmp; } if(!nowflow)break; } return flow; } void work() { memset(fir,-1,sizeof(fir)); memset(inf,0x7f,sizeof(inf)); n=read(),m=read(),s=read(),t=read(); for(re int i=1;i<=m;i++) { LL u1=read(),v1=read(),fl1=llread(); addedge(u1,v1,fl1); } while(bfs()) { for(re int i=1;i<=n;i++) tfir[i]=fir[i]; maxflow+=dfs(s,inf[0]); } write(maxflow); } } t; int main() { t.work(); return 0; }