Flow construction SGU - 176 有源汇有上下界最小流 二分法和回流法
Posted 天道酬勤007
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Flow construction SGU - 176 有源汇有上下界最小流 二分法和回流法相关的知识,希望对你有一定的参考价值。
/** 题目: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 有源汇有上下界最小流 二分法和回流法的主要内容,如果未能解决你的问题,请参考以下文章