1. 程式人生 > >有源匯有上下界可行流/最大流

有源匯有上下界可行流/最大流

inf \n CA log nod ase style 刪除 IV

解析為轉載 :https://www.cnblogs.com/liu-runda/p/6262832.html 博主講的挺好的 有源匯有上下界可行流 模型:現在的網絡有一個源點s和匯點t,求出一個流使得源點的總流出量等於匯點的總流入量,其他的點滿足流量守恒,而且每條邊的流量滿足上界和下界限制. 源點和匯點不滿足流量守恒,這讓我們很難辦,因此我們想辦法把問題轉化成容易處理的每個點都滿足流量守恒的無源匯情況. 為了使源匯點滿足流量守恒,我們需要有邊流入源點s,有邊流出匯點t.註意到源點s的流出量等於匯點t的流入量,我們就可以從匯點t向源點s連一條下界為0上界為無窮大的邊,相當於把從源點s流出的流量再流回來.在這樣的圖中套用上面的算法求出一個可行的循環流,拆掉從匯點t到源點s的邊就得到一個可行的有源匯流. 這裏有一個小問題:最後得到的可行的有源匯流的流量是多少? 可以發現,循環流中一定滿足s流出的總流量=流入s的總流量,假定原圖中沒有邊流入s,那麽s流出的流量就是t到s的無窮邊的流量,也就是s-t可行流的流量.因此我們最後看一下t到s的無窮邊的流量(即dinic跑完之後反向邊的權值)即可知道原圖中有源匯可行流的流量. 例題:LOJ 116 :https://loj.ac/problem/116
#include <iostream>
#include 
<cstring> #include <cstdio> #include <queue> #define mem(a,b) memset(a,b,sizeof(a)) using namespace std; const int maxn = 100010, INF = 0x7fffffff; int d[maxn], head[maxn], in[maxn]; int n, m, s, t; struct node{ int u, v, c, f, next, bz; //bz為標記是否為原圖中的路 1 是 0 不是 }Node[maxn]; void
add(int u, int v, int c, int f,int i, int bz) { Node[i].u = u; Node[i].v = v; Node[i].c = c; Node[i].f = f; Node[i].next = head[u]; head[u] = i; Node[i].bz = bz; } int bfs() { queue<int> Q; mem(d,0); Q.push(s); d[s] = 1; while(!Q.empty()) {
int u = Q.front(); Q.pop(); for(int i=head[u]; i!=-1; i=Node[i].next) { node e = Node[i]; if(!d[e.v] && e.c > e.f) { d[e.v] = d[e.u] + 1; Q.push(e.v); } } } return d[t] != 0; } int dfs(int u, int cap) { if( u == t) return cap; for(int i=head[u]; i!=-1; i=Node[i].next) { node e = Node[i]; if(d[e.v] == d[e.u] + 1 && e.c > e.f) { int V = dfs(e.v, min(cap, e.c - e.f)); if(V > 0) { Node[i].f += V; Node[i^1].f -= V; return V; } } } return 0; } int Dinic(int u) { int ans = 0; while(bfs()) { while(int l = dfs(u, INF)) ans += l; } return ans; } int main() { mem(head,-1); int s_, t_; //源點和匯點 cin>> n >> m >> s_ >> t_; s = 0; t = n+1; // 設置超級源點 和 超級匯點 int sum = 0, cnt = 0; for(int i=0; i<m; i++) { int u, v, b, d; cin>> u >> v >> b >> d; add(u, v, d-b, 0, cnt++,1); add(v, u, 0, 0, cnt++,1); in[v] += b; in[u] -= b; } for(int i=1; i<=n; i++) { if(in[i] > 0) { sum += in[i]; add(s,i,in[i],0,cnt++,0); add(i,s,0,0,cnt++,0); } else { add(i,t,-in[i],0,cnt++,0); add(t,i,0,0,cnt++,0); } } add(t_,s_,INF,0,cnt++,0); //連接匯點和源點 上界為無窮大 下界為0 add(s_,t_,0,0,cnt++,0); if(sum != Dinic(s)) //如果不等於 說明沒有可行流 { cout<< "please go home to sleep" <<endl; } else //如果有可行流 則刪除超級源點和超級匯點 跑一次Dinic 即可 { sum = Node[head[t_]^1].c - Node[head[t_]^1].f; for(int i=0; i<cnt; i++) { if(!Node[i].bz) Node[i].v = 0; } head[s] = head[t] = -1; s = s_; t = t_; printf("%d\n",sum + Dinic(s)); } return 0; }

有源匯有上下界可行流/最大流