1. 程式人生 > >Flow construction SGU - 176 有源匯有上下界最小流 二分法和回流法

Flow construction SGU - 176 有源匯有上下界最小流 二分法和回流法

iostream cor pre ios back max text col 變量存儲

/**

題目:Flow construction SGU - 176
鏈接:https://vjudge.net/problem/SGU-176
題意:
有源匯有上下界的最小流。
給定n個點,m個管道。每個管道給出u,v,z,c。u表示起點,v表示終點,z表示容量,如果c==1,那麽表示還有下界為z。
如果c==0,表示沒有下界。
求從1到n的最小流。
思路:
第一種做法:
轉化為無源匯求超級源S到超級匯T的最大流flow1(此時從s出發的流和為flow1),然後講t到s的邊刪掉(可以使流量等於容量,這樣求t到s的最大流就不會經過他了。)
求t到s的最大流flow2(從s出發的流減少的量).是為了回流,因為原先求flow1的過程,是為了滿足下界的可行流。這個在原圖的可行流可能可以變得更小,通過回流使其縮小。
求t到s的最大流並不會影響原來附加邊的流量。所以保證了是下界滿足的可行流。
然後用flow1-flow2就是結果。

第二種做法:
構造無源匯有上下界的可行流做法,只不過t到s的邊的上下界要改一下。
二分t到s的上界a,下界為0,如果是可行流最小的a便是最小流。如果是求最大流,那麽就是二分t到s的下界a,上界無窮,如果是可行流,那麽最大的a便是最大流。
不懂的看文檔解釋。
https://wenku.baidu.com/view/0f3b691c59eef8c75fbfb35c.html 關於每次二分,處理一次最大流,那麽下次在計算最大流的時候,難道又要重新建圖? 反正是結構體存的,新加一個變量存儲。下次直接從這個變量獲取即可。註意t到s這條邊的修改。或者最後面再加進去。 */ 第一種做法 #include<iostream> #include<cstring> #include<vector> #include<map> #include<cstdio> #include<algorithm> #include
<queue> using namespace std; const int INF = 0x3f3f3f3f; typedef long long LL; const int N = 210;///n+m=1365 int in[N]; int out[N]; struct Edge{ int from, to, cap, flow; Edge(int u,int v,int c,int f):from(u),to(v),cap(c),flow(f){} }; struct Dinic{ int n, m, s, t; vector<Edge> edges; vector
<int> G[N]; bool vis[N]; int d[N]; int cur[N]; void init(int n) { this->n = n; for(int i = 0; i <= n; i++) G[i].clear(); edges.clear(); } void AddEdge(int from,int to,int cap) { edges.push_back(Edge(from,to,cap,0)); edges.push_back(Edge(to,from,0,0)); m = edges.size(); G[from].push_back(m-2); G[to].push_back(m-1); } bool BFS() { memset(vis, 0, sizeof vis); queue<int> Q; Q.push(s); d[s] = 0; vis[s] = 1; while(!Q.empty()) { int x = Q.front(); Q.pop(); for(int i = 0; i < G[x].size(); i++) { Edge &e = edges[G[x][i]]; if(!vis[e.to]&&e.cap>e.flow) { vis[e.to] = 1; d[e.to] = d[x]+1; Q.push(e.to); } } } return vis[t]; } int DFS(int x,int a) { if(x==t||a==0) return a; int flow = 0, f; for(int &i = cur[x]; i < G[x].size(); i++) { Edge& e = edges[G[x][i]]; if(d[x]+1==d[e.to]&&(f=DFS(e.to,min(a,e.cap-e.flow)))>0) { e.flow += f; edges[G[x][i]^1].flow -= f; flow += f; a -= f; if(a==0) break; } } return flow; } int Maxflow(int s,int t) { this->s = s, this->t = t; int flow = 0; while(BFS()) { memset(cur, 0, sizeof cur); flow += DFS(s,INF); } return flow; } }; int dw[N*N]; int main() { int n, m; while(scanf("%d%d",&n,&m)==2) { Dinic dinic; dinic.init(n+1); int u, v, cap, flag;///1,n為普通源匯。 int s = 0, t = n+1;///超級源匯。 memset(dw, 0, sizeof dw); memset(in, 0, sizeof in); memset(out, 0, sizeof out); for(int i = 0; i<m; i++){ scanf("%d%d%d%d",&u,&v,&cap,&flag); if(flag==1){ dw[i] = cap; dinic.AddEdge(u,v,0); out[u]+=cap; in[v]+=cap; }else { dinic.AddEdge(u,v,cap); } } int ts; dinic.AddEdge(n,1,INF); ts = dinic.edges.size()-2; int sum = 0; for(int i = 1; i <= n; i++){ if(in[i]>out[i]){ dinic.AddEdge(s,i,in[i]-out[i]); sum += in[i]-out[i]; } if(in[i]<out[i]){ dinic.AddEdge(i,t,out[i]-in[i]); } } int flow = dinic.Maxflow(s,t); if(flow!=sum){ printf("Impossible\n"); continue; } dinic.edges[ts].cap = dinic.edges[ts].flow;///使其求n到1的最大流無法經過。 int flow2 = dinic.Maxflow(n,1);///回流 printf("%d\n",flow-flow2); for(int i = 0; i < m; i++){ if(i==m-1){ printf("%d\n",dinic.edges[2*i].flow+dw[i]); }else { printf("%d ",dinic.edges[2*i].flow+dw[i]); } } } return 0; } 第二種做法 #include<iostream> #include<cstring> #include<vector> #include<map> #include<cstdio> #include<algorithm> #include<queue> using namespace std; const int INF = 0x3f3f3f3f; typedef long long LL; const int N = 210;///n+m=1365 int in[N]; int out[N]; struct Edge{ int from, to, cap, flow; Edge(int u,int v,int c,int f):from(u),to(v),cap(c),flow(f){} }; struct Dinic{ int n, m, s, t; vector<Edge> edges; vector<int> G[N]; bool vis[N]; int d[N]; int cur[N]; void init(int n) { this->n = n; for(int i = 0; i <= n; i++) G[i].clear(); edges.clear(); } void AddEdge(int from,int to,int cap) { edges.push_back(Edge(from,to,cap,0)); edges.push_back(Edge(to,from,0,0)); m = edges.size(); G[from].push_back(m-2); G[to].push_back(m-1); } bool BFS() { memset(vis, 0, sizeof vis); queue<int> Q; Q.push(s); d[s] = 0; vis[s] = 1; while(!Q.empty()) { int x = Q.front(); Q.pop(); for(int i = 0; i < G[x].size(); i++) { Edge &e = edges[G[x][i]]; if(!vis[e.to]&&e.cap>e.flow) { vis[e.to] = 1; d[e.to] = d[x]+1; Q.push(e.to); } } } return vis[t]; } int DFS(int x,int a) { if(x==t||a==0) return a; int flow = 0, f; for(int &i = cur[x]; i < G[x].size(); i++) { Edge& e = edges[G[x][i]]; if(d[x]+1==d[e.to]&&(f=DFS(e.to,min(a,e.cap-e.flow)))>0) { e.flow += f; edges[G[x][i]^1].flow -= f; flow += f; a -= f; if(a==0) break; } } return flow; } int Maxflow(int s,int t) { this->s = s, this->t = t; int flow = 0; while(BFS()) { memset(cur, 0, sizeof cur); flow += DFS(s,INF); } return flow; } }; int dw[N*N]; int main() { int n, m; while(scanf("%d%d",&n,&m)==2) { Dinic dinic; dinic.init(n+1); int u, v, cap, flag;///1,n為普通源匯。 int s = 0, t = n+1;///超級源匯。 memset(dw, 0, sizeof dw); memset(in, 0, sizeof in); memset(out, 0, sizeof out); for(int i = 0; i<m; i++){ scanf("%d%d%d%d",&u,&v,&cap,&flag); if(flag==1){ dw[i] = cap; dinic.AddEdge(u,v,0); out[u]+=cap; in[v]+=cap; }else { dinic.AddEdge(u,v,cap); } } int sum = 0; for(int i = 1; i <= n; i++){ if(in[i]>out[i]){ dinic.AddEdge(s,i,in[i]-out[i]); sum += in[i]-out[i]; } if(in[i]<out[i]){ dinic.AddEdge(i,t,out[i]-in[i]); } } Dinic Tdinic = dinic; int flow; int lo = 0, hi = INF, mid; while(lo<hi){///最小流,二分上界,取最小值。 mid = (lo+hi)/2; dinic = Tdinic; dinic.AddEdge(n,1,mid); flow = dinic.Maxflow(s,t); if(flow!=sum){ lo = mid+1; }else { hi = mid; } } if(hi==lo){ printf("Impossible\n"); continue; } printf("%d\n",hi); dinic = Tdinic;///註意復原,因為此時的dinic不是最小流為hi的時候的。要重新計算。 dinic.AddEdge(n,1,hi); dinic.Maxflow(s,t); for(int i = 0; i < m; i++){ if(i==m-1){ printf("%d\n",dinic.edges[2*i].flow+dw[i]); }else { printf("%d ",dinic.edges[2*i].flow+dw[i]); } } } return 0; }

Flow construction SGU - 176 有源匯有上下界最小流 二分法和回流法