最大流&最小割&費用流模版
阿新 • • 發佈:2021-07-01
好久都沒有搞部落格了。想認真寫又要準備文化課期末了。
ISAP
- 流程: 原理就是dfs找增廣路。
最基礎的建反向邊以便反悔就不說了。
但是記錄一個dep(dis)表示層數,一開始BFS(從t開始,dis[t]=0)處理最小層數,然後再搜尋增廣路增加限制條件:dis[u]=dis[v]+1,若這樣的v找完了,擴大一層u(即dis[u]++),可能會被回溯到前面的某條新路再次搜中。然後特判一下,如果dis[s]>=n即可結束,因為dis[t]永遠等於0,dis[s]最大為n-1。
gap(cnt)標記很簡單:就是記錄每個dis[]值的個數,若某一中間dis[]值為0,則出現了斷層,理論上就搜不到增廣路了。 - 程式碼:
#include<bits/stdc++.h> using namespace std; const int N=1e4+5; const int inf=0x3f3f3f3f; int nxt[N],to[N],head[N],len[N],num=1; void add_edge(int u,int v,int w) {nxt[++num]=head[u];to[num]=v;len[num]=w;head[u]=num;} int dis[N],n,m,s,t,gap[N]; void bfs() { memset(dis,-1,sizeof(dis)); memset(gap,0,sizeof(gap)); dis[t]=0,gap[0]=1; queue<int>q; q.push(t); while(!q.empty()) { int u=q.front(); q.pop(); for(int i=head[u];i;i=nxt[i]) { int v=to[i]; if(dis[v]!=-1)continue; q.push(v); dis[v]=dis[u]+1,gap[dis[v]]++; } } return; } long long maxflow; long long dfs(int u,long long flow) { if(u==t) return flow; long long used=0; for(int i=head[u];i;i=nxt[i]) { int v=to[i]; if(len[i]&&dis[v]+1==dis[u]) { int tmp=dfs(v,min(1ll*len[i],flow-used)); if(tmp) { len[i]-=tmp; len[i^1]+=tmp; used+=tmp; } if(used==flow)return used; } } --gap[dis[u]]; if(gap[dis[u]]==0)dis[s]=n; dis[u]++,gap[dis[u]]++; return used; } long long ISAP() { maxflow=0; for(bfs();dis[s]<n;maxflow+=dfs(s,inf)); return maxflow; } int main() { scanf("%d%d%d%d",&n,&m,&s,&t); int u,v,w; for(int i=1;i<=m;i++) { scanf("%d%d%d",&u,&v,&w); add_edge(u,v,w); add_edge(v,u,0); } printf("%lld\n",ISAP()); return 0; }
費用流演算法
- 流程:
首先變化在於,找最大流的基礎上滿足最小(大)費用。其次,每條邊上多了費用一變數,因此我們在存反邊時邊的費用為負的原費用
然後我們把EK_BFS找增廣路改為找最短(長)路即可。注意記錄前驅,最後結束時更新邊資訊。 - 程式碼:
#include<stdio.h> #include<bits/stdc++.h> using namespace std; const int N=1e5+5; int mxflow=0,mnval=0,dis[N],head[N],n,m,s,t,to[N],nxt[N],len[N],val[N],ecnt=1,flow[N],pre[N],inf=0x3f3f3f3f; void add_edge(int u,int v,int z,int w) { nxt[++ecnt]=head[u];to[ecnt]=v;len[ecnt]=z;val[ecnt]=w;head[u]=ecnt; nxt[++ecnt]=head[v];to[ecnt]=u;len[ecnt]=0;val[ecnt]=-w;head[v]=ecnt; } bool In_q[N]; queue<int> Q; void init() { for(int i=0;i<=n;i++)dis[i]=flow[i]=inf,In_q[i]=0,pre[i]=0; } bool spfa() { init(); Q.push(s); dis[s]=0,In_q[s]=1; while(!Q.empty()) { int u=Q.front(); Q.pop(); In_q[s]=0; for(int i=head[u];i;i=nxt[i]) { int v=to[i]; if(len[i]&&dis[v]>dis[u]+val[i]) { dis[v]=dis[u]+val[i]; flow[v]=min(flow[u],len[i]); pre[v]=i^1; //記錄終點往前的邊的編號 Q.push(v),In_q[v]=1; } } } if(dis[t]==0x3f3f3f3f) return false; return true; } void mn_valflow() { while(spfa()) { mxflow+=flow[t]; mnval+=flow[t]*dis[t]; int k=t; while(pre[k]) { len[pre[k]]+=flow[t],len[pre[k]^1]-=flow[t]; k=to[pre[k]]; } } } int main() { scanf("%d%d%d%d",&n,&m,&s,&t); for(int i=1;i<=m;i++) { int u,v,z,w; scanf("%d%d%d%d",&u,&v,&z,&w); add_edge(u,v,z,w); } mn_valflow(); printf("%d %d",mxflow,mnval); return 0; }