[kuangbin带你飞]专题十一 网络流
B - Dining
首先二分图的多重匹配匈牙利算法是不行的,无法最优。
直接在构图跑最大流也是不行的:会出现限制。
每一个人拆成两个点,流量为1,增加流量限制,保证不会变大。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <cstdio> #include <cstring> #include <queue> #include <vector> #include <algorithm> using namespace std; typedef double db; const db eps=1e-6; const int inf=0x3f3f3f3f; const int N=1e4+5; int n,m,S,T; struct Dinic{ int head[N],dep[N]; int ecnt; struct edge{ int v,flow,next; }e[N*10]; void init(){ memset(head, -1, sizeof head); ecnt = 0; } void addedge(int u, int v, int flow) { e[ecnt].v=v;e[ecnt].flow=flow;e[ecnt].next=head[u];head[u]=ecnt++; } bool BFS(){//分层图找增广路 memset(dep,0,sizeof dep); queue<int>Q; Q.push(S);dep[S]=1; while(!Q.empty()){ int u=Q.front();Q.pop(); for(int i=head[u];~i;i=e[i].next){ if(e[i].flow&&!dep[e[i].v]){ dep[e[i].v]=dep[u]+1; Q.push(e[i].v); } } } return dep[T]; } int DFS(int u, int f){//推进新流 if(u==T||f==0)return f; int w,used=0; for(int i=head[u];~i;i=e[i].next){ if(e[i].flow&&dep[e[i].v]==dep[u]+1){ w=DFS(e[i].v,min(f,e[i].flow));//多路增广 e[i].flow-=w;e[i^1].flow+=w; used+=w;f-=w; } } if(!used)dep[u]=-1;//炸点优化 return used; } int maxflow() { int ans=0; while(BFS()){ ans+=DFS(S,inf); } return ans; } }dinic; int main(){ int F,D; scanf("%d %d %d",&n,&F,&D); S=0,T=2*n+F+D+10; dinic.init(); for(int i=1;i<=F;i++){ dinic.addedge(S,i,1); dinic.addedge(i,S,0); } // for(int i=1;i<=n;i++) for(int i=1,A,B,v;i<=n;i++){ scanf("%d %d",&A,&B); while(A--){scanf("%d",&v);dinic.addedge(v,i+F,1);dinic.addedge(i+F,v,0);} dinic.addedge(i+F,i+F+n,1); dinic.addedge(i+F+n,i+F,0); while(B--){scanf("%d",&v);dinic.addedge(i+F+n,v+2*n+F,1);dinic.addedge(v+2*n+F,i+F+n,0);} } for(int i=1;i<=D;i++){ dinic.addedge(i+2*n+F,T,1); dinic.addedge(T,i+2*n+F,0); } printf("%d\n",dinic.maxflow()); // system("pause"); return 0; }
D - Going Home
一张网格图,给定若干个人和房子,求所有人走到房子的最小花费。
把人和房子连起来,跑一遍费用流即可。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <cstdio> // #include<bits/stdc++.h> #include <cstring> #include <queue> #include <vector> #include <algorithm> using namespace std; #define pb push_back #define mp make_pair #define fi first #define se second const int inf=0x3f3f3f3f; const int N=1e4+50; int n,m,S,T,maxflow,mincost; struct Dinic{ int head[N],dep[N],dis[N],pre[N],flow[N],last[N]; //dep记录分层图,pre点前驱,flow点的流,last点的前一条边,dis表示费用 bool inq[N]; int ecnt; struct edge{ int v,w,flow,next; }e[N*10]; void init(){ memset(head, -1, sizeof head); ecnt = 0; } void addedge(int u,int v,int flow,int w){ e[ecnt].v=v;e[ecnt].w=w;e[ecnt].flow=flow;e[ecnt].next=head[u];head[u]=ecnt++; } bool spfa(){//spfa找增广路 memset(dis,inf,sizeof dis); memset(flow,inf,sizeof flow); memset(inq,0,sizeof inq); queue<int>Q; Q.push(S);inq[S]=1;dis[S]=0;pre[T]=-1; while(!Q.empty()){ int u=Q.front();Q.pop();inq[u]=0; for(int i=head[u];~i;i=e[i].next){ int v=e[i].v,w=e[i].w; if(e[i].flow>0&&dis[v]>dis[u]+w){ dis[v]=dis[u]+w;pre[v]=u;last[v]=i; flow[v]=min(flow[u],e[i].flow); if(!inq[v]){ Q.push(v);inq[v]=1; } } } } return pre[T]!=-1; } int MCMF(){ maxflow=mincost=0; while(spfa()){//推进新流 int cur=T; maxflow+=flow[T]; mincost+=flow[T]*dis[T]; while(cur!=S){//汇点向前回溯 e[last[cur]].flow-=flow[T]; e[last[cur]^1].flow+=flow[T]; cur=pre[cur]; } } return mincost; } }dinic; char gp[500][500]; vector<pair<int,int> >H,M; int get(int a,int b,int c,int d){ return abs(a-c)+abs(b-d); } int getid(int x,int y){return (x-1)*m+y;} int main(){ while(~scanf("%d %d",&n,&m),n+m){ dinic.init(); H.clear(); M.clear(); for(int i=1;i<=n;i++){ getchar(); for(int j=1;j<=m;j++){ scanf("%c",&gp[i][j]); if(gp[i][j]=='m')M.pb(mp(i,j)); else if(gp[i][j]=='H')H.pb(mp(i,j)); } } // cout<<"mp "<<endl; // for(int i=1;i<=n;i++){ // for(int j=1;j<=m;j++){ // cout<<gp[i][j]; // } // cout<<endl; // } // cout<<endl; // cout<<"M :"<<M.size()<<endl; // cout<<"H "<<H.size()<<endl; S=0,T=n*m+10; for(int i=0;i<H.size();i++){ int x=H[i].fi,y=H[i].se; int id=getid(x,y); dinic.addedge(S,id,1,0); dinic.addedge(id,S,0,0); } for(int i=0;i<H.size();i++){ int hx=H[i].fi,hy=H[i].se; int hs=getid(hx,hy); for(int j=0;j<M.size();j++){ int mx=M[j].fi,my=M[j].se; int me=getid(mx,my); int cost=get(hx,hy,mx,my); dinic.addedge(hs,me,1,cost); dinic.addedge(me,hs,0,-cost); } } for(int i=0;i<M.size();i++){ int x=M[i].fi,y=M[i].se; int id=getid(x,y); dinic.addedge(id,T,1,0); dinic.addedge(T,id,0,0); } printf("%d\n",dinic.MCMF()); } // system("pause"); return 0; }
E - Minimum Cost
m个货店,n个人,每个人需要若干个物品,每个点可以提供若干个物品,求满足所有人需求的最小花费。
开始想直接暴力建 kn 个人点,km个店点,一共有5000个点,TLE
首先特判,如果k个节点提供的小于需要的,直接输出-1。
考虑k种商品相互独立,互不关联,跑k次费用流,将结果累加即可。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <cstdio> // #include<bits/stdc++.h> #include <cstring> #include <queue> #include <vector> #include <algorithm> using namespace std; #define pb push_back #define mp make_pair #define fi first #define se second const int inf=0x3f3f3f3f; const int N=1e5+50; int n,m,K,S,T,maxflow,mincost; struct Dinic{ int head[N],dep[N],dis[N],pre[N],flow[N],last[N]; //dep记录分层图,pre点前驱,flow点的流,last点的前一条边,dis表示费用 bool inq[N]; int ecnt; struct edge{ int v,w,flow,next; }e[N*10]; void init(){ // for(int i=0;i<K*(m+n);i++)head[i]=-1; // memset(head, -1, sizeof head); for(int i=0;i<=2*m*n*K*K;i++)head[i]=-1; ecnt = 0; } void addedge(int u,int v,int flow,int w){ e[ecnt].v=v;e[ecnt].w=w;e[ecnt].flow=flow;e[ecnt].next=head[u];head[u]=ecnt++; } bool spfa(){//spfa找增广路 memset(dis,inf,sizeof dis); memset(flow,inf,sizeof flow); memset(inq,0,sizeof inq); queue<int>Q; Q.push(S);inq[S]=1;dis[S]=0;pre[T]=-1; while(!Q.empty()){ int u=Q.front();Q.pop();inq[u]=0; for(int i=head[u];~i;i=e[i].next){ int v=e[i].v,w=e[i].w; if(e[i].flow>0&&dis[v]>dis[u]+w){ dis[v]=dis[u]+w;pre[v]=u;last[v]=i; flow[v]=min(flow[u],e[i].flow); if(!inq[v]){ Q.push(v);inq[v]=1; } } } } return pre[T]!=-1; } int MCMF(){ maxflow=mincost=0; while(spfa()){//推进新流 int cur=T; maxflow+=flow[T]; mincost+=flow[T]*dis[T]; while(cur!=S){//汇点向前回溯 e[last[cur]].flow-=flow[T]; e[last[cur]^1].flow+=flow[T]; cur=pre[cur]; } } return mincost; } }dinic; int need[100],supply[100]; int mt[100][100][100],from[100][100],to[100][100]; bool check(){ for(int i=1;i<=K;i++)if(supply[i]<need[i])return 0; return 1; } int main(){ while(~scanf("%d %d %d",&n,&m,&K),n+m+K){ memset(need,0,sizeof need); memset(supply,0,sizeof supply); for(int i=1;i<=n;i++){ for(int k=1;k<=K;k++){ scanf("%d",&to[i][k]); need[k]+=to[i][k]; } } for(int i=1;i<=m;i++){ for(int k=1;k<=K;k++){ scanf("%d",&from[i][k]); supply[k]+=from[i][k]; } } for(int k=1;k<=K;k++){ for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ scanf("%d",&mt[k][i][j]); } } } if(!check()){puts("-1");continue;} int ans=0; for(int k=1;k<=K;k++){ dinic.init();S=0;T=m+n+50; for(int i=1;i<=m;i++){ dinic.addedge(S,i,from[i][k],0); dinic.addedge(i,S,0,0); } for(int i=1;i<=m;i++){ for(int j=1;j<=n;j++){ dinic.addedge(i,m+j,inf,mt[k][j][i]); dinic.addedge(m+j,i,0,-mt[k][j][i]); } } for(int i=1;i<=n;i++){ dinic.addedge(m+i,T,to[i][k],0); dinic.addedge(T,m+i,0,0); } ans+=dinic.MCMF(); } printf("%d\n",ans); } // system("pause"); return 0; }
G - Island Transport
。。看错题了,题目保证流没有相交,
TLE的原因是因为本来是个无向边,非要拆成四条边肯定多了两倍时间 ,实际拆成两条边就行了。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#pragma comment(linker, "/STACK:1024000000,1024000000") #include <cstdio> #include<bits/stdc++.h> #include <cstring> #include <queue> #include <vector> #include <algorithm> using namespace std; #define pb push_back #define mp make_pair #define fi first #define se second const int inf=0x3f3f3f3f; const int N=1e5+50; int n,m,S,T; struct Dinic{ int head[N],dep[N]; int ecnt; struct edge{ int v,flow,next; }e[N*10]; void init(){ memset(head, -1, sizeof head); ecnt = 0; } void addedge(int u, int v, int flow) { e[ecnt].v=v;e[ecnt].flow=flow;e[ecnt].next=head[u];head[u]=ecnt++; } bool BFS(){//分层图找增广路 memset(dep,0,sizeof dep); queue<int>Q; Q.push(S);dep[S]=1; while(!Q.empty()){ int u=Q.front();Q.pop(); for(int i=head[u];~i;i=e[i].next){ if(e[i].flow&&!dep[e[i].v]){ dep[e[i].v]=dep[u]+1; Q.push(e[i].v); } } } return dep[T]; } int DFS(int u, int f){//推进新流 if(u==T||f==0)return f; int w,used=0; for(int i=head[u];~i;i=e[i].next){ if(e[i].flow&&dep[e[i].v]==dep[u]+1){ w=DFS(e[i].v,min(f,e[i].flow));//多路增广 e[i].flow-=w;e[i^1].flow+=w; used+=w;f-=w; } } if(!used)dep[u]=-1;//炸点优化 return used; } int maxflow() { int ans=0; while(BFS()){ ans+=DFS(S,inf); } return ans; } }dinic; int main(){ int t; scanf("%d",&t); while(t--){ scanf("%d %d",&n,&m); int Min=inf,Max=-inf; for(int i=1,x,y;i<=n;i++){ scanf("%d %d",&x,&y); if(x>Max){Max=x;T=i;} if(x<Min){Min=x;S=i;} } dinic.init(); for(int i=1,u,v,f;i<=m;i++){ scanf("%d %d %d",&u,&v,&f); dinic.addedge(u,v,f); // dinic.addedge(v,u,0); dinic.addedge(v,u,f); // dinic.addedge(u,v,0); } printf("%d\n",dinic.maxflow()); } // system("pause"); return 0; }
想的太多,做的太少;