最大流练习题
一,无源网络流的建模
https://www.luogu.org/problemnew/show/P1231
题意,给你n1本书,n2本练习册,n3本答案,给你这些书和答案对应关系,问你最多能组成多少本书册。
由于需要书,练习册,答案三件套才能组成完整书册,将书复制成两份,一份与练习册建立边,一份与答案建立边,
建立源点和汇点,跑一边最大流就能解决
#include<bits/stdc++.h> using namespace std; const int maxn=100000; const int INF=0x3fffffff; struct Dinic { struct Edge { int from,to,cap,flow; }; int n,m,s,t; vector<Edge>edges; vector<int>G[maxn]; bool vis[maxn]; int d[maxn]; int cur[maxn]; 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)//x为当前节点,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)//下一层次加1并且残留量不为零 { 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; } }; Dinic dc; int main() { int n1,n2,n3,m1,m2,m3; scanf("%d %d %d",&n1,&n2,&n3); scanf("%d",&m1); for(int i=1; i<=m1; i++)//练习册指书的边 { int x,y; scanf("%d %d",&x,&y); dc.AddEdge(y,x+n2,1); } scanf("%d",&m2); for(int i=1; i<=m2; i++)//书指向答案的边 { int x,y; scanf("%d %d",&x,&y); dc.AddEdge(x+n2+n1,2*n1+n2+y,1); } for(int i=1; i<=n2; i++)//建立源点指向练习册的边 { dc.AddEdge(0,i,1); } for(int i=1; i<=n1; i++)//建立书指向书的边 { dc.AddEdge(n2+i,n2+n1+i,1); } int end=n1*2+n2+n3+1; for(int i=1; i<=n3; i++)//建立书指向终点的边 { dc.AddEdge(n2+2*n1+i,end,1); } printf("%d",dc.Maxflow(0,end)); return 0; }
二,建立二分图跑网络流
题意,给你个二分图,每次选择一条边会给每个点加一个度,问能否使得点的度的范围在[l,r]之间。
https://nanti.jisuanke.com/t/31447
#include<iostream> #include<cstdio> #include<cstring> #include<queue> using namespace std; const int maxn=20010; const int N=0x3f3f3f3f; const int INF=0x3fffffff; int degree[maxn]; int u[maxn]; int v[maxn]; struct Dinic { struct Edge { int from,to,cap,flow; }; int n,m,s,t; vector<Edge>edges; vector<int>G[maxn]; bool vis[maxn]; int d[maxn]; int cur[maxn]; 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)//x为当前节点,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)//下一层次加1并且残留量不为零 { 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; } }; void init() { memset(degree,0,sizeof(degree)); return; } int main() { int cas,i,l,r,minn,maxx,res,sum,n,m,k; cas=1; while(scanf("%d%d%d",&n,&m,&k)!=EOF) { init(); scanf("%d%d",&l,&r); for(i=1; i<=k; i++) { scanf("%d%d",&u[i],&v[i]); v[i]+=n; degree[u[i]]++,degree[v[i]]++; } minn=N,maxx=0; for(i=1; i<=n+m; i++) { minn=min(minn,degree[i]); maxx=max(maxx,degree[i]); } if(l<=minn) { if(maxx<=r) printf("Case %d: Yes\n",cas++); else { for(i=1; i<=k; i++) { if(degree[u[i]]>r&°ree[v[i]]>r) { degree[u[i]]--,degree[v[i]]--; } } int source=0,sink=n+m+1; Dinic dc; for(i=1; i<=k; i++) { if(degree[u[i]]>r&°ree[v[i]]>l) dc.AddEdge(u[i],v[i],1); else if(degree[v[i]]>r&°ree[u[i]]>l) dc.AddEdge(v[i],u[i],1); } sum=0; for(i=1; i<=n+m; i++) { if(degree[i]>r) { dc.AddEdge(source,i,degree[i]-r); sum+=(degree[i]-r); } else if(degree[i]>l) { dc.AddEdge(i,sink,degree[i]-l); } } res=dc.Maxflow(source,sink); if(res==sum) printf("Case %d: Yes\n",cas++); else printf("Case %d: No\n",cas++); } } else printf("Case %d: No\n",cas++); } return 0; }
三,无源上下界网络流问题
模板题
题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=1314
#include<bits/stdc++.h> using namespace std; const int maxn=100005; const int INF=0x3fffffff; struct Dinic { struct Edge { int from,to,cap,flow; }; int n,m,s,t; vector<Edge>edges; vector<int>G[maxn]; bool vis[maxn]; int d[maxn]; int cur[maxn]; void init(int n) { for (int i=0; i<=n+1 ; 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)//x为当前节点,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)//下一层次加1并且残留量不为零 { 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 d[maxn]; int l[maxn]; Dinic dc; int main() { int t; scanf("%d",&t); while(t--) { int n,m; scanf("%d%d",&n,&m); dc.init(n); memset(d,0,sizeof d); for (int i=0; i<m ; i++ ) { int u,v,low,up; scanf("%d%d%d%d",&u,&v,&low,&up); d[u]+=low; d[v]-=low; l[i]=low; dc.AddEdge(u,v,up-low); } int ss=0; for (int i=1; i<=n ; i++ ) { if(d[i]>0) { dc.AddEdge(i,n+1,d[i]); ss+=d[i]; } else dc.AddEdge(0,i,-d[i]); } if(dc.Maxflow(0,n+1)==ss) { cout<<"YES"<<endl; for (int i=0; i<m ; i++ ) { cout<<dc.edges[2*i].flow+l[i]<<endl; } } else cout<<"NO"<<endl; } return 0; }
四,二分图,建立源点的有源上下界网络流
题目和上面的二分图一样,解决方法是建立一个源点连向左图,每条边的范围要是[l,r],右图连向汇点,范围一样,左图与右图的边的范围是[0,1],就转化为上面的模板题了。
#include<bits/stdc++.h> using namespace std; const int maxn=100005; const int INF=0x3fffffff; struct Dinic { struct Edge { int from,to,cap,flow; }; int n,m,s,t; vector<Edge>edges; vector<int>G[maxn]; bool vis[maxn]; int d[maxn]; int cur[maxn]; void init(int n) { for (int i=0; i<=maxn ; 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)//x为当前节点,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)//下一层次加1并且残留量不为零 { 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 d[maxn]; int l[maxn]; Dinic dc; int main() { int n,m,k; int acm=1; while(~scanf("%d%d%d",&n,&m,&k)) { dc.init(n); int l,r; scanf("%d%d",&l,&r); memset(d,0,sizeof d); for(int i=1; i<=n; i++) { dc.AddEdge(0,n,r-l); d[0]+=l; d[i]-=l; } for(int i=1; i<=k; i++) { int u,v; scanf("%d%d",&u,&v); dc.AddEdge(u,v+n,1); } for(int i=n+1; i<=n+m; i++) { dc.AddEdge(i,n+m+1,r-l); d[i]+=l; d[n+m+1]-=l; } int ss=0; for (int i=0; i<=m+n+1 ; i++ ) { if(d[i]>0) { dc.AddEdge(i,n+m+3,d[i]); ss+=d[i]; } else dc.AddEdge(n+m+2,i,-d[i]); } dc.AddEdge(n+m+1,0,0x3fffffff); if(dc.Maxflow(n+m+2,n+m+3)==ss) { printf("Case %d: Yes\n",acm++); } else printf("Case %d: No\n",acm++); } return 0; }