专题训练之最大流
EK模板
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<queue> 5 #include<vector> 6 using namespace std; 7 const int maxn=205; 8 const int maxm=205; 9 const int inf=1e9; 10 struct edge{ 11 int from,to,flow; 12 edge(int u,int v,int f):from(u),to(v),flow(f) {} 13 }; 14 int n,s,t; 15 vector<edge>E; //大小为边数的两倍 ,用于存边 16 vector<int>G[maxn]; //存每个点对应的边 17 int a[maxn],p[maxn]; //a代表每个点的流出量,p用于记录进入该点的那条边的编号 18 19 void init() //初始化 20 { 21 for ( int i=0;i<n;i++ ) G[i].clear(); 22 E.clear(); 23 } 24 25 void addedge(int from,int to,int flow) 26 { 27 E.push_back(edge(from,to,flow)); 28 E.push_back(edge(to,from,0)); 29 int m=E.size(); 30 G[from].push_back(m-2); 31 G[to].push_back(m-1); 32 } 33 34 int maxflow() 35 { 36 int flow=0; 37 for ( ;; ) { 38 memset(a,0,sizeof(a)); 39 a[s]=inf; 40 queue<int>que; 41 que.push(s); 42 while ( !que.empty() ) { 43 int u=que.front(); 44 que.pop(); 45 for ( int i=0;i<G[u].size();i++ ) { 46 int v=G[u][i]; 47 edge& e=E[v]; 48 if ( e.flow>0 && !a[e.to] ) { 49 p[e.to]=v; 50 a[e.to]=min(a[u],e.flow); 51 que.push(e.to); 52 } 53 } 54 if ( a[t] ) break; 55 } 56 if ( !a[t] ) break; 57 for ( int u=t;u!=s;u=E[p[u]].from ) { 58 E[p[u]].flow-=a[t]; 59 E[p[u]^1].flow+=a[t]; 60 } 61 flow+=a[t]; 62 } 63 return flow; 64 }
DINIC模板
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<queue> 5 #include<vector> 6 using namespace std; 7 const int maxn=205; 8 const int maxm=205; 9 const int inf=1e9; 10 struct edge{ 11 int from,to,flow; 12 edge(int u,int v,int f):from(u),to(v),flow(f) {} 13 }; 14 int n,s,t; 15 vector<edge>E; 16 vector<int>G[maxn]; 17 bool vis[maxn]; //检测在bfs中能否到达终点t,每次BFS都要初始化一次 18 int d[maxn],cur[maxn]; //d代表点所在的层级,cur代表该点有多少条边已经考虑过了 19 20 void init() 21 { 22 for ( int i=0;i<=n;i++ ) G[i].clear(); 23 E.clear(); 24 } 25 26 void addedge(int from,int to,int flow) 27 { 28 E.push_back(edge(from,to,flow)); 29 E.push_back(edge(to,from,0)); 30 int m=E.size(); 31 G[from].push_back(m-2); 32 G[to].push_back(m-1); 33 } 34 35 bool BFS() 36 { 37 memset(vis,false,sizeof(vis)); 38 queue<int>que; 39 que.push(s); 40 d[s]=0; 41 vis[s]=true; 42 while ( !que.empty() ) { 43 int u=que.front(); 44 que.pop(); 45 for ( int i=0;i<G[u].size();i++ ) { 46 edge& e=E[G[u][i]]; 47 if ( !vis[e.to] && e.flow>0 ) { 48 d[e.to]=d[u]+1; 49 vis[e.to]=true; 50 que.push(e.to); 51 } 52 } 53 } 54 return vis[t]; 55 } 56 57 int DFS(int u,int a) //a为目前为止所有边中的最小残量 58 { 59 if ( u==t || a==0 ) return a; 60 int flow=0,f; 61 for ( int& i=cur[u];i<G[u].size();i++ ) { 62 edge& e=E[G[u][i]]; 63 if ( d[u]+1==d[e.to] && (f=DFS(e.to,min(a,e.flow)))>0 ) { 64 e.flow-=f; 65 E[G[u][i]^1].flow+=f; 66 flow+=f; 67 a-=f; 68 if ( a==0 ) break; 69 } 70 } 71 return flow; 72 } 73 74 int maxflow() 75 { 76 int flow=0; 77 while ( BFS() ) { 78 memset(cur,0,sizeof(cur)); 79 flow+=DFS(s,inf); 80 } 81 return flow; 82 } 83
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<queue> 5 #include<vector> 6 using namespace std; 7 const int maxn=250; 8 const int maxm=105; 9 const int inf=1e9; 10 int n,s,t; 11 struct edge{ 12 int from,to,cap,flow; 13 edge(int u,int v,int c,int f):from(u),to(v),cap(c),flow(f) {} 14 }; 15 vector<edge>E; 16 vector<int>G[maxn]; 17 bool vis[maxn]; 18 int d[maxn],cur[maxn]; 19 20 void init() 21 { 22 for ( int i=0;i<=n;i++ ) G[i].clear(); 23 E.clear(); 24 } 25 26 void addedge(int from,int to,int cap) 27 { 28 E.push_back(edge(from,to,cap,0)); 29 E.push_back(edge(to,from,0,0)); 30 int m=E.size(); 31 G[from].push_back(m-2); 32 G[to].push_back(m-1); 33 } 34 35 bool BFS() 36 { 37 memset(vis,false,sizeof(vis)); 38 queue<int>que; 39 que.push(s); 40 d[s]=0; 41 vis[s]=true; 42 while ( !que.empty() ) { 43 int u=que.front(); 44 que.pop(); 45 for ( int i=0;i<G[u].size();i++ ) { 46 edge& e=E[G[u][i]]; 47 if ( !vis[e.to] && (e.cap-e.flow)>0 ) { 48 d[e.to]=d[u]+1; 49 vis[e.to]=true; 50 que.push(e.to); 51 } 52 } 53 } 54 return vis[t]; 55 } 56 57 int DFS(int u,int a) 58 { 59 if ( u==t || a==0 ) return a; 60 int flow=0,f; 61 for ( int& i=cur[u];i<G[u].size();i++ ) { 62 edge& e=E[G[u][i]]; 63 if ( d[u]+1==d[e.to] && (f=DFS(e.to,min(a,e.cap-e.flow)))>0 ) { 64 e.flow+=f; 65 E[G[u][i]^1].flow-=f; 66 flow+=f; 67 a-=f; 68 if ( a==0 ) break; 69 } 70 } 71 return flow; 72 } 73 74 int maxflow() 75 { 76 int flow=0; 77 while ( BFS() ) { 78 memset(cur,0,sizeof(cur)); 79 flow+=DFS(s,inf); 80 } 81 return flow; 82 }
//注意初始化不要忘记
HDOJ练习题
1.(HDOJ1532) http://acm.hdu.edu.cn/showproblem.php?pid=1532
裸模板题
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<queue> 5 #include<vector> 6 using namespace std; 7 const int maxn=205; 8 const int maxm=205; 9 const int inf=1e9; 10 struct edge{ 11 int from,to,flow; 12 edge(int u,int v,int f):from(u),to(v),flow(f) {} 13 }; 14 int n,s,t; 15 vector<edge>E; 16 vector<int>G[maxn]; 17 int a[maxn],p[maxn]; 18 19 void init() 20 { 21 for ( int i=0;i<n;i++ ) G[i].clear(); 22 E.clear(); 23 } 24 25 void addedge(int from,int to,int flow) 26 { 27 E.push_back(edge(from,to,flow)); 28 E.push_back(edge(to,from,0)); 29 int m=E.size(); 30 G[from].push_back(m-2); 31 G[to].push_back(m-1); 32 } 33 34 int maxflow() 35 { 36 int flow=0; 37 for ( ;; ) { 38 memset(a,0,sizeof(a)); 39 a[s]=inf; 40 queue<int>que; 41 que.push(s); 42 while ( !que.empty() ) { 43 int u=que.front(); 44 que.pop(); 45 for ( int i=0;i<G[u].size();i++ ) { 46 int v=G[u][i]; 47 edge& e=E[v]; 48 if ( e.flow>0 && !a[e.to] ) { 49 p[e.to]=v; 50 a[e.to]=min(a[u],e.flow); 51 que.push(e.to); 52 } 53 } 54 if ( a[t] ) break; 55 } 56 if ( !a[t] ) break; 57 for ( int u=t;u!=s;u=E[p[u]].from ) { 58 E[p[u]].flow-=a[t]; 59 E[p[u]^1].flow+=a[t]; 60 } 61 flow+=a[t]; 62 } 63 return flow; 64 } 65 66 int main() 67 { 68 int i,j,k,x,y,z,m; 69 while ( scanf("%d%d",&m,&n)!=EOF ) { 70 init(); 71 for ( i=0;i<m;i++ ) { 72 scanf("%d%d%d",&x,&y,&z); 73 x--;y--; 74 addedge(x,y,z); 75 } 76 s=0; 77 t=n-1; 78 int ans=maxflow(); 79 printf("%d\n",ans); 80 } 81 return 0; 82 }
#include<cstdio> #include<cstring> #include<algorithm> #include<queue> #include<vector> using namespace std; const int maxn=205; const int maxm=205; const int inf=1e9; struct edge{ int from,to,flow; edge(int u,int v,int f):from(u),to(v),flow(f) {} }; int n,s,t; vector<edge>E; vector<int>G[maxn]; bool vis[maxn]; int d[maxn],cur[maxn]; void init() { for ( int i=0;i<n;i++ ) G[i].clear(); E.clear(); } void addedge(int from,int to,int flow) { E.push_back(edge(from,to,flow)); E.push_back(edge(to,from,0)); int m=E.size(); G[from].push_back(m-2); G[to].push_back(m-1); } bool BFS() { memset(vis,false,sizeof(vis)); queue<int>que; que.push(s); d[s]=0; vis[s]=true; while ( !que.empty() ) { int u=que.front(); que.pop(); for ( int i=0;i<G[u].size();i++ ) { edge& e=E[G[u][i]]; if ( !vis[e.to] && e.flow>0 ) { d[e.to]=d[u]+1; vis[e.to]=true; que.push(e.to); } } } return vis[t]; } int DFS(int u,int a) { if ( u==t || a==0 ) return a; int flow=0,f; for ( int& i=cur[u];i<G[u].size();i++ ) { edge& e=E[G[u][i]]; if ( d[u]+1==d[e.to] && (f=DFS(e.to,min(a,e.flow)))>0 ) { e.flow-=f; E[G[u][i]^1].flow+=f; flow+=f; a-=f; if ( a==0 ) break; } } return flow; } int maxflow() { int flow=0; while ( BFS() ) { memset(cur,0,sizeof(cur)); flow+=DFS(s,inf); } return flow; } int main() { int i,j,k,x,y,z,m; while ( scanf("%d%d",&m,&n)!=EOF ) { init(); for ( i=0;i<m;i++ ) { scanf("%d%d%d",&x,&y,&z); x--;y--; addedge(x,y,z); } s=0; t=n-1; int ans=maxflow(); printf("%d\n",ans); } return 0; }
2.(HDOJ3572) http://acm.hdu.edu.cn/showproblem.php?pid=3572
题意:有n项任务和m台机器,对于任意任务i都有要求在[si,ei]这段时间中用掉pi小时才算完成任务。求是否可能完成任务
分析:1.思考:考虑什么变量会是点,什么变量会成为边的容量,终点的条件和最大流有什么关系。
2.对于源点和汇点:因为无论是对于项目还是机器还是时间来说在各自范围内等价的,没有任何的特殊,所有需要设置一个超级源点和超级汇点。
3.对于任意任务i,都需要pi时间,就像是任务i是由pi个小任务组成的一样,而小任务和时间是等价的,所以是将所有的任务和时间看作点,类似于二分图,时间一边,任务一边,各自独立;
4.因为机器的数量(动力)与工作的效率有关,即可以将机器的数量看作是源点到每个“时间”点的边的容量(类似于对于每个时间点来说能完成多少数量的小任务)。而每个任务到汇点的边的容量等于该任务所需的pi时间。而时间与任务之间边的容量与工作时间范围[si,ei]有关,每天对于需要的任务都只能提供1的小任务/时间/效率/动力。
5.最终的判断条件就是所有任务所需要的时间(小任务)是否为maxflow
注意:数组要开大一点,大小并只是任务数,而是任务数+时间。
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<queue> 5 #include<vector> 6 using namespace std; 7 const int maxn=2005; 8 const int maxm=1005; 9 const int inf=1e9; 10 struct edge{ 11 int from,to,flow; 12 edge(int u,int v,int f):from(u),to(v),flow(f) {} 13 }; 14 struct node{ 15 int s,e,p; 16 }arr[maxn]; 17 int n,s,t; 18 vector<edge>E; 19 vector<int>G[maxn]; 20 bool vis[maxn]; 21 int d[maxn],cur[maxn]; 22 23 void init() 24 { 25 for ( int i=0;i<=n;i++ ) G[i].clear(); 26 E.clear(); 27 } 28 29 void addedge(int u,int v,int f) 30 { 31 E.push_back(edge(u,v,f)); 32 E.push_back(edge(v,u,0)); 33 int m=E.size(); 34 G[u].push_back(m-2); 35 G[v].push_back(m-1); 36 } 37 38 bool BFS() 39 { 40 memset(vis,false,sizeof(vis)); 41 queue<int>que; 42 que.push(s); 43 vis[s]=true; 44 d[s]=0; 45 while ( !que.empty() ) { 46 int u=que.front(); 47 que.pop(); 48 for ( int i=0;i<G[u].size();i++ ) { 49 edge& e=E[G[u][i]]; 50 if ( !vis[e.to] && e.flow>0 ) { 51 d[e.to]=d[u]+1; 52 vis[e.to]=true; 53 que.push(e.to); 54 } 55 } 56 } 57 return vis[t]; 58 } 59 60 int DFS(int u,int a) 61 { 62 if ( u==t || a==0 ) return a; 63 int flow=0,f; 64 for ( int& i=cur[u];i<G[u].size();i++ ) { 65 edge& e=E[G[u][i]]; 66 if ( d[u]+1==d[e.to] && (f=DFS(e.to,min(a,e.flow)))>0 ) { 67 e.flow-=f; 68 E[G[u][i]^1].flow+=f; 69 flow+=f; 70 a-=f; 71 if ( a==0 ) break; 72 } 73 } 74 return flow; 75 } 76 77 int maxflow() 78 { 79 int flow=0; 80 while ( BFS() ) { 81 memset(cur,0,sizeof(cur)); 82 flow+=DFS(s,inf); 83 } 84 return flow; 85 } 86 87 int main() 88 { 89 int T,i,j,k,m,N,M,h,ans,sum; 90 scanf("%d",&T); 91 for ( h=1;h<=T;h++ ) { 92 scanf("%d%d",&N,&m); 93 init(); 94 sum=0; 95 n=0; 96 for ( i=1;i<=N;i++ ) { 97 scanf("%d%d%d",&arr[i].p,&arr[i].s,&arr[i].e); 98 sum+=arr[i].p; 99 n=max(arr[i].e,n); 100 } 101 n=n+N+1; 102 s=0; 103 t=n; 104 for ( i=N+1;i<n;i++ ) addedge(s,i,m); 105 for ( i=1;i<=N;i++ ) { 106 addedge(i,n,arr[i].p); 107 for ( j=arr[i].s;j<=arr[i].e;j++ ) addedge(j+N,i,1); 108 } 109 ans=maxflow(); 110 printf("Case %d: ",h); 111 if ( ans==sum ) printf("Yes\n\n"); 112 else printf("No\n\n"); 113 } 114 return 0; 115 }
3.(HDOJ2732)http://acm.hdu.edu.cn/showproblem.php?pid=2732
题意:给出n和d,n代表地图有几行,d代表蜥蜴能跳的最大距离。然后给出两个地图,第一个图为每根柱子能承受跳跃的次数,第二个图为蜥蜴所在的位置。
错误思路:因为对于每根柱子来说同时只能有一只蜥蜴,所以考虑拆点,一根柱子能承受几次跳跃就拆成几个点。设置超级源点指向蜥蜴初始位置所在的点,容量为1.将那些可以直接跳出地图的点连向超级汇点,容量也为1,将那些能相互到达的点也连上容量为1的边。这样给出的第4个样例输出0没有办法满足
正确的做法:http://blog.csdn.net/u013480600/article/details/38964749
注意:1.输出格式,was和were,要不要加s
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<queue> 5 #include<vector> 6 #include<cmath> 7 using namespace std; 8 const int maxn=805; 9 const int maxm=25; 10 const int inf=1e9; 11 struct edge{ 12 int from,to,flow; 13 edge(int u,int v,int f):from(u),to(v),flow(f) {} 14 }; 15 struct node{ 16 int x; 17 int y; 18 }arr[maxn]; 19 int n,s,t; 20 vector<edge>E; 21 vector<int>G[maxn]; 22 bool vis[maxn]; 23 int d[maxn],cur[maxn]; 24 char mp1[maxm][maxm],mp2[maxm][maxm]; 25 26 void init() 27 { 28 for ( int i=0;i<=n;i++ ) G[i].clear(); 29 E.clear(); 30 } 31 32 void addedge(int from,int to,int flow) 33 { 34 E.push_back(edge(from,to,flow)); 35 E.push_back(edge(to,from,0)); 36 int m=E.size(); 37 G[from].push_back(m-2); 38 G[to].push_back(m-1); 39 } 40 41 bool BFS() 42 { 43 memset(vis,false,sizeof(vis)); 44 queue<int>que; 45 que.push(s); 46 d[s]=0; 47 vis[s]=true; 48 while ( !que.empty() ) { 49 int u=que.front(); 50 que.pop(); 51 for ( int i=0;i<G[u].size();i++ ) { 52 edge& e=E[G[u][i]]; 53 if ( !vis[e.to] && e.flow>0 ) { 54 d[e.to]=d[u]+1; 55 vis[e.to]=true; 56 que.push(e.to); 57 } 58 } 59 } 60 return vis[t]; 61 } 62 63 int DFS(int u,int a) 64 { 65 if ( u==t || a==0 ) return a; 66 int flow=0,f; 67 for ( int& i=cur[u];i<G[u].size();i++ ) { 68 edge& e=E[G[u][i]]; 69 if ( d[u]+1==d[e.to] && (f=DFS(e.to,min(a,e.flow)))>0 ) { 70 e.flow-=f; 71 E[G[u][i]^1].flow+=f; 72 flow+=f; 73 a-=f; 74 if ( a==0 ) break; 75 } 76 } 77 return flow; 78 } 79 80 int maxflow() 81 { 82 int flow=0; 83 while ( BFS() ) { 84 memset(cur,0,sizeof(cur)); 85 flow+=DFS(s,inf); 86 } 87 return flow; 88 } 89 90 int dist(node a,node b) 91 { 92 return abs(a.x-b.x)+abs(a.y-b.y); 93 } 94 95 int main() 96 { 97 int T,i,j,k,h,x,y,z,N,M,d,u,v,l,cnt,dis,sum,ans; 98 scanf("%d",&T); 99 for ( h=1;h<=T;h++ ) { 100 scanf("%d%d",&N,&d); 101 init(); 102 for ( i=1;i<=N;i++ ) scanf("%s",mp1[i]+1); 103 M=0; 104 for ( i=1;mp1[1][i];i++ ) M++; 105 s=0; 106 n=N*M*2+1; 107 t=n; 108 cnt=0; 109 sum=0; 110 for ( i=1;i<=N;i++ ) scanf("%s",mp2[i]+1); 111 for ( i=1;i<=N;i++ ) { 112 for ( j=1;j<=M;j++ ) { 113 x=mp1[i][j]-'0'; 114 u=(i-1)*M+j; 115 v=u+M*N; 116 if ( x!=0 ) { 117 addedge(u,v,x); 118 if ( i<=d || j<=d || N-i<d || M-j<d ) addedge(v,t,inf); 119 arr[++cnt].x=i; 120 arr[cnt].y=j; 121 } 122 if ( mp2[i][j]=='L' ) { 123 addedge(s,u,1); 124 sum++; 125 } 126 } 127 } 128 for ( i=1;i<=cnt;i++ ) { 129 for ( j=1;j<=cnt;j++ ) { 130 u=(arr[i].x-1)*M+arr[i].y; 131 v=(arr[j].x-1)*M+arr[j].y; 132 if ( i==j ) continue; 133 dis=dist(arr[i],arr[j]); 134 if ( dis<=d ) addedge(u+M*N,v,inf); 135 } 136 } 137 ans=sum-maxflow(); 138 if ( ans==0 ) printf("Case #%d: no lizard was left behind.\n",h); 139 else if ( ans==1 ) printf("Case #%d: 1 lizard was left behind.\n",h); 140 else printf("Case #%d: %d lizards were left behind.\n",h,ans); 141 } 142 return 0; 143 }
4.(HDOJ1569)http://acm.hdu.edu.cn/showproblem.php?pid=1569
此题涉及二分图的最大点权独立集,补充知识:
二分图最小点覆盖和最大独立集都可以转化为最大匹配求解。在这个基础上,把每个点赋予一个非负的权值,这两个问题就转化为:二分图最小点权覆盖和二分图最大点权独立集。
二分图最小点权覆盖
从x或者y集合中选取一些点,使这些点覆盖所有的边,并且选出来的点的权值尽可能小。
建模:原二分图中的边(u,v)替换为容量为INF的有向边(u,v),设立源点s和汇点t,将s和x集合中的点相连,容量为该点的权值;将y中的点同t相连,容量为该点的权值。在新图上求最大流,最大流量即为最小点权覆盖的权值和。
二分图最大点权独立集
在二分图中找到权值和最大的点集,使得它们之间两两没有边。其实它是最小点权覆盖的对偶问题。答案=总权值-最小点覆盖集。
具体证明参考胡波涛的论文:https://wenku.baidu.com/view/986baf00b52acfc789ebc9a9.html
分析:对于特殊图(网格形状)来说,某个点只与其四周的的几个点相连,建立联系(建为INF的边,原因是认为使其不为最小割)。将整个图进行黑白染色(按行坐标+纵坐标的奇偶染色),一种颜色的点和超级源点S建为点权的边,另一种颜色的点和超级汇点也建容量为点权的边。最后的值为总权值-最大流
注意:1.数组不要开太小
2.只有于S相连的点才访问他的四周然后建边,而不是任何颜色的点都可以建边的
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<queue> 5 #include<vector> 6 using namespace std; 7 const int maxn=2550; 8 const int maxm=55; 9 const int inf=1e9; 10 struct edge{ 11 int from,to,flow; 12 edge(int u,int v,int f):from(u),to(v),flow(f) {} 13 }; 14 int n,s,t; 15 vector<edge>E; 16 vector<int>G[maxn]; 17 bool vis[maxn]; 18 int d[maxn],cur[maxn]; 19 int mp[maxm][maxm]; 20 int dir[4][2]={{1,0},{-1,0},{0,1},{0,-1}}; 21 22 void init() 23 { 24 for ( int i=0;i<=n;i++ ) G[i].clear(); 25 E.clear(); 26 } 27 28 void addedge(int from,int to,int flow) 29 { 30 E.push_back(edge(from,to,flow)); 31 E.push_back(edge(to,from,0)); 32 int m=E.size(); 33 G[from].push_back(m-2); 34 G[to].push_back(m-1); 35 } 36 37 bool BFS() 38 { 39 memset(vis,false,sizeof(vis)); 40 queue<int>que; 41 que.push(s); 42 d[s]=0; 43 vis[s]=true; 44 while ( !que.empty() ) { 45 int u=que.front(); 46 que.pop(); 47 for ( int i=0;i<G[u].size();i++ ) { 48 edge& e=E[G[u][i]]; 49 if ( !vis[e.to] && e.flow>0 ) { 50 d[e.to]=d[u]+1; 51 vis[e.to]=true; 52 que.push(e.to); 53 } 54 } 55 } 56 return vis[t]; 57 } 58 59 int DFS(int u,int a) 60 { 61 if ( u==t || a==0 ) return a; 62 int flow=0,f; 63 for ( int& i=cur[u];i<G[u].size();i++ ) { 64 edge& e=E[G[u][i]]; 65 if ( d[u]+1==d[e.to] && (f=DFS(e.to,min(a,e.flow)))>0 ) { 66 e.flow-=f; 67 E[G[u][i]^1].flow+=f; 68 flow+=f; 69 a-=f; 70 if ( a==0 ) break; 71 } 72 } 73 return flow; 74 } 75 76 int maxflow() 77 { 78 int flow=0; 79 while ( BFS() ) { 80 memset(cur,0,sizeof(cur)); 81 flow+=DFS(s,inf); 82 } 83 return flow; 84 } 85 86 int main() 87 { 88 int N,M,i,j,k,x,y,z,u,v,sum,ans,dx,dy; 89 while ( scanf("%d%d",&N,&M)!=EOF ) { 90 init(); 91 sum=0; 92 for ( i=1;i<=N;i++ ) { 93 for ( j=1;j<=M;j++ ) { 94 scanf("%d",&mp[i][j]); 95 sum+=mp[i][j]; 96 } 97 } 98 s=0; 99 n=N*M+1; 100 t=n; 101 for ( i=1;i<=N;i++ ) { 102 for ( j=1;j<=M;j++ ) { 103 u=(i-1)*M+j; 104 if ( (i+j)%2==0 ) { 105 addedge(s,u,mp[i][j]); 106 for ( k=0;k<4;k++ ) { 107 x=i+dir[k][0]; 108 y=j+dir[k][1]; 109 if ( x>0 && x<=N && y>0 && y<=M ) { 110 v=(x-1)*M+y; 111 addedge(u,v,inf); 112 } 113 } 114 } 115 else addedge(u,t,mp[i][j]); 116 } 117 } 118 ans=sum-maxflow(); 119 printf("%d\n",ans); 120 } 121 return 0; 122 }
5.(HDOJ4289)http://acm.hdu.edu.cn/showproblem.php?pid=4289
题意:输入N,M,N代表有多少点,M代表有多少边,起点为s,,终点为t。每个点都有一个费用表示的含义是切断和这个点的所有边。然后给出M条双向边。先求最小的费用使得s不能到达t
分析:考虑到点有限制,所以采用拆点的方法,将每个点拆成两个点,中间连一条容量为点权的边。同时起点为s前一个点,终点为t后一个点(这样可以保证s,t也可被消灭)。然后连通两个点的一条无向边,分别表示为x->y+N和y->x+N容量都为inf的边,即一个点的出点和另一个点的入点相连,因为对边没有任何的限制,所以将边设置为inf。
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<queue> 5 #include<vector> 6 using namespace std; 7 const int maxn=505; 8 const int maxm=205; 9 const int inf=1e9; 10 struct edge{ 11 int from,to,flow; 12 edge(int u,int v,int f):from(u),to(v),flow(f) {} 13 }; 14 int n,s,t; 15 vector<edge>E; 16 vector<int>G[maxn]; 17 bool vis[maxn]; 18 int d[maxn],cur[maxn]; 19 20 void init() 21 { 22 for ( int i=0;i<=n;i++ ) G[i].clear(); 23 E.clear(); 24 } 25 26 void addedge(int from,int to,int flow) 27 { 28 E.push_back(edge(from,to,flow)); 29 E.push_back(edge(to,from,0)); 30 int m=E.size(); 31 G[from].push_back(m-2); 32 G[to].push_back(m-1); 33 } 34 35 bool BFS() 36 { 37 memset(vis,false,sizeof(vis)); 38 queue<int>que; 39 que.push(s); 40 d[s]=0; 41 vis[s]=true; 42 while ( !que.empty() ) { 43 int u=que.front(); 44 que.pop(); 45 for ( int i=0;i<G[u].size();i++ ) { 46 edge& e=E[G[u][i]]; 47 if ( !vis[e.to] && e.flow>0 ) { 48 d[e.to]=d[u]+1; 49 vis[e.to]=true; 50 que.push(e.to); 51 } 52 } 53 } 54 return vis[t]; 55 } 56 57 int DFS(int u,int a) 58 { 59 if ( u==t || a==0 ) return a; 60 int flow=0,f; 61 for ( int& i=cur[u];i<G[u].size();i++ ) { 62 edge& e=E[G[u][i]]; 63 if ( d[u]+1==d[e.to] && (f=DFS(e.to,min(a,e.flow)))>0 ) { 64 e.flow-=f; 65 E[G[u][i]^1].flow+=f; 66 flow+=f; 67 a-=f; 68 if ( a==0 ) break; 69 } 70 } 71 return flow; 72 } 73 74 int maxflow() 75 { 76 int flow=0; 77 while ( BFS() ) { 78 memset(cur,0,sizeof(cur)); 79 flow+=DFS(s,inf); 80 } 81 return flow; 82 } 83 84 int main() 85 { 86 int N,i,j,k,x,y,z,M,ans; 87 while ( scanf("%d%d",&N,&M)!=EOF ) { 88 scanf("%d%d",&x,&y); 89 s=x-1; 90 t=y-1+N; 91 n=2*N; 92 init(); 93 for ( i=0;i<N;i++ ) { 94 scanf("%d",&x); 95 addedge(i,i+N,x); 96 } 97 for ( i=1;i<=M;i++ ) { 98 scanf("%d%d",&x,&y); 99 x--;y--; 100 addedge(x+N,y,inf); 101 addedge(y+N,x,inf); 102 } 103 ans=maxflow(); 104 printf("%d\n",ans); 105 } 106 return 0; 107 }
6.(HDOJ3605)http://acm.hdu.edu.cn/showproblem.php?pid=3605
题意:地球上有n个人,有m个星球,每个人对每个星球都有适应/不适应两种情况,只有适应才能移居到那个星球。每个星球所能承受的人也是有限的。问是否所有人都能够移民。
分析:初看以为是个裸模板,类似二分图,地球上的人一边,星球一边,第i个人对j个星球适应就连一条容量为1的边,起点和每个人连一条容量为1的边,每个星球和终点连一条容量为该星球承受最大人数的边。但是因为n很大所有一定会超时,这时发现m<=10,所以所有的点最多只有1024种情况,所以采用类似于状态压缩的办法进行缩点,将适应情况一样的人放在一个点,这样点的最大数量为1023(人数集合)+2(起点和终点)+10(星球个数)=1035,在这个范围内还是可以求解的。同时注意当有人对所有星期都不适应的时候直接输出NO。
#include<cstdio> #include<cstring> #include<algorithm> #include<queue> #include<vector> using namespace std; const int maxn=1200; const int maxm=205; const int inf=1e9; struct edge{ int from,to,flow; edge(int u,int v,int f):from(u),to(v),flow(f) {} }; int n,s,t; vector<edge>E; vector<int>G[maxn]; bool vis[maxn]; int d[maxn],cur[maxn],p[maxn],num[12]; void init() { for ( int i=0;i<=n;i++ ) G[i].clear(); E.clear(); } void addedge(int from,int to,int flow) { E.push_back(edge(from,to,flow)); E.push_back(edge(to,from,0)); int m=E.size(); G[from].push_back(m-2); G[to].push_back(m-1); } bool BFS() { memset(vis,false,sizeof(vis)); queue<int>que; que.push(s); d[s]=0; vis[s]=true; while ( !que.empty() ) { int u=que.front(); que.pop(); for ( int i=0;i<G[u].size();i++ ) { edge& e=E[G[u][i]]; if ( !vis[e.to] && e.flow>0 ) { d[e.to]=d[u]+1; vis[e.to]=true; que.push(e.to); } } } return vis[t]; } int DFS(int u,int a) { if ( u==t || a==0 ) return a; int flow=0,f; for ( int& i=cur[u];i<G[u].size();i++ ) { edge& e=E[G[u][i]]; if ( d[u]+1==d[e.to] && (f=DFS(e.to,min(a,e.flow)))>0 ) { e.flow-=f; E[G[u][i]^1].flow+=f; flow+=f; a-=f; if ( a==0 ) break; } } return flow; } int maxflow() { int flow=0; while ( BFS() ) { memset(cur,0,sizeof(cur)); flow+=DFS(s,inf); } return flow; } int main() { int N,M,i,j,k,x,y,z,m,ans; while ( scanf("%d%d",&N,&m)!=EOF ) { memset(p,0,sizeof(p)); for ( i=0;i<N;i++ ) { x=0; for ( j=0;j<m;j++ ) { scanf("%d",&y); if ( y==1 ) x+=(1<<j); } p[x]++; } for ( i=0;i<m;i++ ) scanf("%d",&num[i]); if ( p[0]!=0 ) { printf("NO\n"); continue; } s=0; n=(1<<m)+m; t=n; init(); for ( i=1;i<(1<<m);i++ ) addedge(s,i,p[i]); for ( i=0;i<m;i++ ) addedge((1<<m)+i,t,num[i]); for ( i=1;i<(1<<m);i++ ) { for ( j=0;j<m;j++ ) { if ( i&(1<<j) ) addedge(i,j+(1<<m),p[i]); } } ans=maxflow(); if ( ans==N ) printf("YES\n"); else printf("NO\n"); } return 0; }
7.(HDOJ2883)http://acm.hdu.edu.cn/showproblem.php?pid=2883
题意:有n个顾客,单位时间内能烧m块烤肉。对于第i个顾客有,si,ni,ei,ti,分别表示顾客在第si来(第一次提供烤肉已经是si+1了),ni表示顾客点的烤肉的数量,ei表示顾客在ei离开,ti表示该烤肉烧一块要ti的时间。现在求是否能满足所有客户的要求(判满流)。
分析:本题实质上同2(HDOJ3572),那个题目中的天数对应这里的时间段(将某一时间段缩为一个点),那里的任务相当于这里的顾客。建边:起点于时间段建容量为pi*m的边(pi为该时间段的长度,m为单位时间烤肉的个数),人与终点建容量为ni*ti的边。因为这题si,ei的范围非常大,但是顾客数<=200,所有对应有至多200个起点,200个终点,将其离散化(方法是将所有起点和终点的时间都存入数组,从小到大进行排列,数组中相邻的不相同的两项构成一个时间区间)。对于顾客i来说,遍历所有的时间区间,当时间段被包含在[si,ei]之间则这两个点之间可以建一条为pi*m的边(注意这里一定是m,而不是min(ni,m),因为题目中给定一块肉也可以分开考,若没有这个条件,则对于第i个人来说一天最多烤ni块肉)。
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<queue> 5 #include<vector> 6 using namespace std; 7 const int maxn=1505; 8 const int maxm=205; 9 const int inf=1e9; 10 struct edge{ 11 int from,to,flow; 12 edge(int u,int v,int f):from(u),to(v),flow(f) {} 13 }; 14 int n,s,t; 15 vector<edge>E; 16 vector<int>G[maxn]; 17 bool vis[maxn]; 18 int d[maxn],cur[maxn]; 19 struct node{ 20 int s; 21 int n; 22 int e; 23 int t; 24 }arr[maxn]; 25 struct node_{ 26 int x; 27 int y; 28 int p; 29 }arr_[maxn]; 30 int num[maxn]; 31 32 void init() 33 { 34 for ( int i=0;i<=n;i++ ) G[i].clear(); 35 E.clear(); 36 } 37 38 void addedge(int from,int to,int flow) 39 { 40 E.push_back(edge(from,to,flow)); 41 E.push_back(edge(to,from,0)); 42 int m=E.size(); 43 G[from].push_back(m-2); 44 G[to].push_back(m-1); 45 } 46 47 bool BFS() 48 { 49 memset(vis,false,sizeof(vis)); 50 queue<int>que; 51 que.push(s); 52 d[s]=0; 53 vis[s]=true; 54 while ( !que.empty() ) { 55 int u=que.front(); 56 que.pop(); 57 for ( int i=0;i<G[u].size();i++ ) { 58 edge& e=E[G[u][i]]; 59 if ( !vis[e.to] && e.flow>0 ) { 60 d[e.to]=d[u]+1; 61 vis[e.to]=true; 62 que.push(e.to); 63 } 64 } 65 } 66 return vis[t]; 67 } 68 69 int DFS(int u,int a) 70 { 71 if ( u==t || a==0 ) return a; 72 int flow=0,f; 73 for ( int& i=cur[u];i<G[u].size();i++ ) { 74 edge& e=E[G[u][i]]; 75 if ( d[u]+1==d[e.to] && (f=DFS(e.to,min(a,e.flow)))>0 ) { 76 e.flow-=f; 77 E[G[u][i]^1].flow+=f; 78 flow+=f; 79 a-=f; 80 if ( a==0 ) break; 81 } 82 } 83 return flow; 84 } 85 86 int maxflow() 87 { 88 int flow=0; 89 while ( BFS() ) { 90 memset(cur,0,sizeof(cur)); 91 flow+=DFS(s,inf); 92 } 93 return flow; 94 } 95 96 int main() 97 { 98 int N,M,i,j,k,x,y,z,m,ans,sum,cnt; 99 while ( scanf("%d%d",&N,&m)!=EOF ) { 100 sum=0; 101 for ( i=1;i<=N;i++ ) { 102 scanf("%d%d%d%d",&x,&arr[i].n,&arr[i].e,&arr[i].t); 103 arr[i].s=x; 104 num[i]=arr[i].s; 105 num[i+N]=arr[i].e; 106 sum+=arr[i].n*arr[i].t; 107 } 108 sort(num+1,num+2*N+1); 109 cnt=0; 110 for ( i=1;i<2*N;i++ ) { 111 if ( num[i]==num[i+1] ) continue; 112 arr_[++cnt].x=num[i]; 113 arr_[cnt].y=num[i+1]; 114 arr_[cnt].p=num[i+1]-num[i]; 115 } 116 s=0; 117 n=N+cnt+1; 118 t=n; 119 init(); 120 for ( i=1;i<=cnt;i++ ) addedge(s,i,m*arr_[i].p); 121 for ( i=1;i<=N;i++ ) addedge(i+cnt,t,arr[i].n*arr[i].t); 122 for ( i=1;i<=N;i++ ) { 123 for ( j=1;j<=cnt;j++ ) { 124 x=m; 125 if ( arr[i].s<=arr_[j].x && arr[i].e>=arr_[j].y ) addedge(j,i+cnt,arr_[j].p*x); 126 } 127 } 128 ans=maxflow(); 129 if ( ans==sum ) printf("Yes\n"); 130 else printf("No\n"); 131 } 132 return 0; 133 }
8.(HDOJ4292)http://acm.hdu.edu.cn/showproblem.php?pid=4292
挑战程序设计竞赛上的例题,将食物放一边,饮料放一边,将每个人拆成两个点。食物和起点之间连容量为食物数量的边,饮料和终点连容量为饮料数量的边,人和人之间连容量为1的边。人喜欢哪种食物就将食物和人的前一个点建1的边,人喜欢哪种饮料就将人后一个点和饮料建边。注意是谁指向谁
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<queue> 5 #include<vector> 6 using namespace std; 7 const int maxn=1005; 8 const int maxm=205; 9 const int inf=1e9; 10 struct edge{ 11 int from,to,flow; 12 edge(int u,int v,int f):from(u),to(v),flow(f) {} 13 }; 14 int n,s,t; 15 vector<edge>E; 16 vector<int>G[maxn]; 17 bool vis[maxn]; 18 int d[maxn],cur[maxn],D[maxn],F[maxn]; 19 char mp[maxn][maxn]; 20 21 void init() 22 { 23 for ( int i=0;i<=n;i++ ) G[i].clear(); 24 E.clear(); 25 } 26 27 void addedge(int from,int to,int flow) 28 { 29 E.push_back(edge(from,to,flow)); 30 E.push_back(edge(to,from,0)); 31 int m=E.size(); 32 G[from].push_back(m-2); 33 G[to].push_back(m-1); 34 } 35 36 bool BFS() 37 { 38 memset(vis,false,sizeof(vis)); 39 queue<int>que; 40 que.push(s); 41 d[s]=0; 42 vis[s]=true; 43 while ( !que.empty() ) { 44 int u=que.front(); 45 que.pop(); 46 for ( int i=0;i<G[u].size();i++ ) { 47 edge& e=E[G[u][i]]; 48 if ( !vis[e.to] && e.flow>0 ) { 49 d[e.to]=d[u]+1; 50 vis[e.to]=true; 51 que.push(e.to); 52 } 53 } 54 } 55 return vis[t]; 56 } 57 58 int DFS(int u,int a) 59 { 60 if ( u==t || a==0 ) return a; 61 int flow=0,f; 62 for ( int& i=cur[u];i<G[u].size();i++ ) { 63 edge& e=E[G[u][i]]; 64 if ( d[u]+1==d[e.to] && (f=DFS(e.to,min(a,e.flow)))>0 ) { 65 e.flow-=f; 66 E[G[u][i]^1].flow+=f; 67 flow+=f; 68 a-=f; 69 if ( a==0 ) break; 70 } 71 } 72 return flow; 73 } 74 75 int maxflow() 76 { 77 int flow=0; 78 while ( BFS() ) { 79 memset(cur,0,sizeof(cur)); 80 flow+=DFS(s,inf); 81 } 82 return flow; 83 } 84 85 int main() 86 { 87 int N,M,i,j,k,f,l,x,y,z,ans; 88 while ( scanf("%d%d%d",&N,&f,&l)!=EOF ) { 89 s=0; 90 n=f+l+2*N+1; 91 t=n; 92 init(); 93 for ( i=1;i<=f;i++ ) { 94 scanf("%d",&F[i]); 95 addedge(s,i,F[i]); 96 } 97 for ( i=1;i<=l;i++ ) { 98 scanf("%d",&D[i]); 99 addedge(f+2*N+i,t,D[i]); 100 } 101 for ( i=1;i<=N;i++ ) addedge(i+f,i+f+N,1); 102 for ( i=1;i<=N;i++ ) scanf("%s",mp[i]+1); 103 for ( i=1;i<=N;i++ ) { 104 for ( j=1;j<=f;j++ ) { 105 if ( mp[i][j]=='Y' ) addedge(j,f+i,1); 106 } 107 } 108 for ( i=1;i<=N;i++ ) scanf("%s",mp[i]+1); 109 for ( i=1;i<=N;i++ ) { 110 for ( j=1;j<=l;j++ ) { 111 if ( mp[i][j]=='Y' ) addedge(N+f+i,j+f+2*N,1); 112 } 113 } 114 ans=maxflow(); 115 printf("%d\n",ans); 116 } 117 return 0; 118 }
9.(HDOJ3416)http://acm.hdu.edu.cn/showproblem.php?pid=3416
题意:求有几条最短路,每条路走过一次以后就不能再走。
分析:刚开始没做出来。想的是利用类似最小费用流中的写法,将边与边的距离等效为费用,记录每次的费用,当某一次的费用与第一次不同时,则该次不是最短路。但是会超时
正确的关键是:沿最短路进行最大流,只将最短路上的边(满足dist[u]==dist[v]+d(v,u))加入最大流的网络中
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<queue> 5 #include<vector> 6 using namespace std; 7 const int maxn=1050; 8 const int maxm=205; 9 const int inf=1e9; 10 struct Edge{ 11 int v; 12 int cost; 13 Edge(int _v=0,int _cost=0):v(_v),cost(_cost){} 14 }; 15 vector<Edge>EE[maxn]; 16 struct edge{ 17 int from,to,flow; 18 edge(int u,int v,int f):from(u),to(v),flow(f) {} 19 }; 20 int n,s,t; 21 vector<edge>E; 22 vector<int>G[maxn]; 23 bool vis[maxn]; 24 int d[maxn],cur[maxn],dist[maxn]; 25 26 void addedge_(int u,int v,int w) 27 { 28 EE[u].push_back(Edge(v,w)); 29 } 30 31 void SPFA() 32 { 33 memset(vis,false,sizeof(vis)); 34 for( int i=1;i<=n;i++ ) dist[i]=inf; 35 vis[s]=true; 36 dist[s]=0; 37 queue<int>q; 38 q.push(s); 39 while ( !q.empty() ) { 40 int u=q.front(); 41 q.pop(); 42 vis[u]=false; 43 for ( int i=0;i<EE[u].size();i++ ) { 44 int v=EE[u][i].v; 45 if ( dist[v]>dist[u]+EE[u][i].cost ) { 46 dist[v]=dist[u]+EE[u][i].cost; 47 if ( !vis[v] ) { 48 vis[v]=true; 49 q.push(v); 50 } 51 } 52 } 53 } 54 } 55 56 void init() 57 { 58 for ( int i=0;i<=n;i++ ) G[i].clear(); 59 E.clear(); 60 } 61 62 void addedge(int from,int to,int flow) 63 { 64 E.push_back(edge(from,to,flow)); 65 E.push_back(edge(to,from,0)); 66 int m=E.size(); 67 G[from].push_back(m-2); 68 G[to].push_back(m-1); 69 } 70 71 bool BFS() 72 { 73 memset(vis,false,sizeof(vis)); 74 queue<int>que; 75 que.push(s); 76 d[s]=0; 77 vis[s]=true; 78 while ( !que.empty() ) { 79 int u=que.front(); 80 que.pop(); 81 for ( int i=0;i<G[u].size();i++ ) { 82 edge& e=E[G[u][i]]; 83 if ( !vis[e.to] && e.flow>0 ) { 84 d[e.to]=d[u]+1; 85 vis[e.to]=true; 86 que.push(e.to); 87 } 88 } 89 } 90 return vis[t]; 91 } 92 93 int DFS(int u,int a) 94 { 95 if ( u==t || a==0 ) return a; 96 int flow=0,f; 97 for ( int& i=cur[u];i<G[u].size();i++ ) { 98 edge& e=E[G[u][i]]; 99 if ( d[u]+1==d[e.to] && (f=DFS(e.to,min(a,e.flow)))>0 ) { 100 e.flow-=f; 101 E[G[u][i]^1].flow+=f; 102 flow+=f; 103 a-=f; 104 if ( a==0 ) break; 105 } 106 } 107 return flow; 108 } 109 110 int maxflow() 111 { 112 int flow=0; 113 while ( BFS() ) { 114 memset(cur,0,sizeof(cur)); 115 flow+=DFS(s,inf); 116 } 117 return flow; 118 } 119 120 int main() 121 { 122 int T,i,j,k,x,y,z,m,u,v,dd,ans; 123 scanf("%d",&T); 124 while ( T-- ) { 125 scanf("%d%d",&n,&m); 126 for ( i=1;i<=n;i++ ) EE[i].clear(); 127 while ( m-- ) { 128 scanf("%d%d%d",&x,&y,&z); 129 addedge_(x,y,z); 130 } 131 scanf("%d%d",&s,&t); 132 SPFA(); 133 init(); 134 for ( i=1;i<=n;i++ ) { 135 for ( j=0;j<EE[i].size();j++ ) { 136 v=EE[i][j].v; 137 dd=EE[i][j].cost; 138 if ( dist[i]+dd==dist[v] ) addedge(i,v,1); 139 } 140 } 141 ans=maxflow(); 142 printf("%d\n",ans); 143 } 144 return 0; 145 }
10.(HDOJ3081)http://acm.hdu.edu.cn/showproblem.php?pid=3081
题意:有N个女孩N个男孩,给出M对关系表示女孩选择男孩为男朋友,再给出f对关系,表示两个女孩之间为好朋友,男朋友可以互相选择
错误想法:设立bool型数组line[i][j],为true表示第i个女生和第j个男生可以为男女朋友的关系,然后对于f对女生之间的关系采用并查集。最后s到女生的点建容量为N的边,男生到t建容量为N的边,男女之间有关系的建容量为1的边,跑最大流,最终答案为男生流向t中的边的流量的最小值。
样例:1 3 4 0 1 2 2 1 2 3 3 2 答案应该是0,但用上述方法答案为1。
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<queue> 5 #include<vector> 6 using namespace std; 7 const int maxn=250; 8 const int maxm=105; 9 const int inf=1e9; 10 bool line[maxn][maxn]; 11 int F[maxn]; 12 int n,s,t; 13 struct edge{ 14 int from,to,cap,flow; 15 edge(int u,int v,int c,int f):from(u),to(v),cap(c),flow(f) {} 16 }; 17 vector<edge>E; 18 vector<int>G[maxn]; 19 bool vis[maxn]; 20 int d[maxn],cur[maxn]; 21 22 int find(int x) 23 { 24 if ( F[x]==-1 ) return x; 25 return F[x]=find(F[x]); 26 } 27 28 void merge(int x,int y) 29 { 30 int fx=find(x); 31 int fy=find(y); 32 if ( fx!=fy ) F[fx]=fy; 33 } 34 35 void init() 36 { 37 for ( int i=0;i<=n;i++ ) G[i].clear(); 38 E.clear(); 39 } 40 41 void addedge(int from,int to,int cap) 42 { 43 E.push_back(edge(from,to,cap,0)); 44 E.push_back(edge(to,from,0,0)); 45 int m=E.size(); 46 G[from].push_back(m-2); 47 G[to].push_back(m-1); 48 } 49 50 bool BFS() 51 { 52 memset(vis,false,sizeof(vis)); 53 queue<int>que; 54 que.push(s); 55 d[s]=0; 56 vis[s]=true; 57 while ( !que.empty() ) { 58 int u=que.front(); 59 que.pop(); 60 for ( int i=0;i<G[u].size();i++ ) { 61 edge& e=E[G[u][i]]; 62 if ( !vis[e.to] && (e.cap-e.flow)>0 ) { 63 d[e.to]=d[u]+1; 64 vis[e.to]=true; 65 que.push(e.to); 66 } 67 } 68 } 69 return vis[t]; 70 } 71 72 int DFS(int u,int a) 73 { 74 if ( u==t || a==0 ) return a; 75 int flow=0,f; 76 for ( int& i=cur[u];i<G[u].size();i++ ) { 77 edge& e=E[G[u][i]]; 78 if ( d[u]+1==d[e.to] && (f=DFS(e.to,min(a,e.cap-e.flow)))>0 ) { 79 e.flow+=f; 80 E[G[u][i]^1].flow-=f; 81 flow+=f; 82 a-=f; 83 if ( a==0 ) break; 84 } 85 } 86 return flow; 87 } 88 89 int maxflow() 90 { 91 int flow=0; 92 while ( BFS() ) { 93 memset(cur,0,sizeof(cur)); 94 flow+=DFS(s,inf); 95 } 96 return flow; 97 } 98 99 int main() 100 { 101 int i,j,k,x,y,z,N,M,T,f,ans,sum; 102 scanf("%d",&T); 103 while ( T-- ) { 104 scanf("%d%d%d",&N,&M,&f); 105 memset(F,-1,sizeof(F)); 106 memset(line,false,sizeof(line)); 107 while ( M-- ) { 108 scanf("%d%d",&x,&y); 109 line[x][y]=true; 110 } 111 while ( f-- ) { 112 scanf("%d%d",&x,&y); 113 merge(x,y); 114 } 115 s=0; 116 n=2*N+1; 117 t=n; 118 init(); 119 for ( i=1;i<=N;i++ ) { 120 for ( j=1;j<=N;j++ ) { 121 if ( i==j ) continue; 122 if ( find(i)==find(j) ) { 123 for ( k=1;k<=N;k++ ) { 124 if ( line[j][k] ) line[i][k]=true; 125 } 126 } 127 } 128 } 129 for ( i=1;i<=N;i++ ) { 130 for ( j=1;j<=N;j++ ) { 131 if ( line[i][j] ) addedge(i,j+N,1); //注意一定要放在外面! 132 } 133 } 134 for ( i=1;i<=N;i++ ) { 135 addedge(s,i,N); 136 addedge(i+N,t,N); 137 } 138 sum=maxflow(); 139 ans=inf; 140 for ( i=1+N;i<=2*N;i++ ) { 141 for ( j=0;j<G[i].size();j++ ) { 142 edge& e=E[G[i][j]]; 143 if ( e.flow>=0 ) ans=min(ans,e.flow); 144 } 145 } 146 printf("%d\n",ans); 147 } 148 return 0; 149 }
正确思路:二分+并查集+最大流
二分是在原流的基础上再进行最大流,而不是每次都从新开始。如何从本来已经跑过的最大流基础上继续进行?(待补充)
每次从新开始不会TLE,二分枚举的s到女生边的容量,最后判断是否满流即可
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<queue> 5 #include<vector> 6 using namespace std; 7 const int maxn=250; 8 const int maxm=105; 9 const int inf=1e9; 10 bool line[maxn][maxn]; 11 int F[maxn]; 12 int n,s,t,N; 13 struct edge{ 14 int from,to,cap,flow; 15 edge(int u,int v,int c,int f):from(u),to(v),cap(c),flow(f) {} 16 }; 17 vector<edge>E; 18 vector<int>G[maxn]; 19 bool vis[maxn]; 20 int d[maxn],cur[maxn]; 21 22 int find(int x) 23 { 24 if ( F[x]==-1 ) return x; 25 return F[x]=find(F[x]); 26 } 27 28 void merge(int x,int y) 29 { 30 int fx=find(x); 31 int fy=find(y); 32 if ( fx!=fy ) F[fx]=fy; 33 } 34 35 void init() 36 { 37 for ( int i=0;i<=n;i++ ) G[i].clear(); 38 E.clear(); 39 } 40 41 void addedge(int from,int to,int cap) 42 { 43 E.push_back(edge(from,to,cap,0)); 44 E.push_back(edge(to,from,0,0)); 45 int m=E.size(); 46 G[from].push_back(m-2); 47 G[to].push_back(m-1); 48 } 49 50 bool BFS() 51 { 52 memset(vis,false,sizeof(vis)); 53 queue<int>que; 54 que.push(s); 55 d[s]=0; 56 vis[s]=true; 57 while ( !que.empty() ) { 58 int u=que.front(); 59 que.pop(); 60 for ( int i=0;i<G[u].size();i++ ) { 61 edge& e=E[G[u][i]]; 62 if ( !vis[e.to] && (e.cap-e.flow)>0 ) { 63 d[e.to]=d[u]+1; 64 vis[e.to]=true; 65 que.push(e.to); 66 } 67 } 68 } 69 return vis[t]; 70 } 71 72 int DFS(int u,int a) 73 { 74 if ( u==t || a==0 ) return a; 75 int flow=0,f; 76 for ( int& i=cur[u];i<G[u].size();i++ ) { 77 edge& e=E[G[u][i]]; 78 if ( d[u]+1==d[e.to] && (f=DFS(e.to,min(a,e.cap-e.flow)))>0 ) { 79 e.flow+=f; 80 E[G[u][i]^1].flow-=f; 81 flow+=f; 82 a-=f; 83 if ( a==0 ) break; 84 } 85 } 86 return flow; 87 } 88 89 int maxflow() 90 { 91 int flow=0; 92 while ( BFS() ) { 93 memset(cur,0,sizeof(cur)); 94 flow+=DFS(s,inf); 95 } 96 return flow; 97 } 98 99 bool judge(int x) 100 { 101 int ans=maxflow(); 102 if ( ans==N*x ) return true; 103 return false; 104 } 105 106 int main() 107 { 108 int i,j,k,x,y,z,M,T,f,ans,sum,l,r,mid,la; 109 scanf("%d",&T); 110 while ( T-- ) { 111 scanf("%d%d%d",&N,&M,&f); 112 memset(F,-1,sizeof(F)); 113 memset(line,false,sizeof(line)); 114 while ( M-- ) { 115 scanf("%d%d",&x,&y); 116 line[x][y]=true; 117 } 118 while ( f-- ) { 119 scanf("%d%d",&x,&y); 120 merge(x,y); 121 } 122 s=0; 123 n=2*N+1; 124 t=n; 125 for ( i=1;i<=N;i++ ) { 126 for ( j=1;j<=N;j++ ) { 127 if ( i==j ) continue; 128 if ( find(i)==find(j) ) { 129 for ( k=1;k<=N;k++ ) { 130 if ( line[j][k] ) line[i][k]=true; 131 } 132 } 133 } 134 } 135 for ( i=1;i<=N;i++ ) { 136 for ( j=1;j<=N;j++ ) { 137 if ( line[i][j] ) addedge(i,j+N,1); 138 } 139 } 140 l=0; 141 r=n+1; 142 while ( r-l>1 ) { 143 mid=(l+r)/2; 144 init(); 145 for ( i=1;i<=N;i++ ) { 146 for ( j=1;j<=N;j++ ) { 147 if ( line[i][j] ) addedge(i,j+N,1); 148 } 149 } 150 for ( i=1;i<=N;i++ ) { 151 addedge(s,i,mid); 152 addedge(i+N,t,mid); 153 } 154 if ( judge(mid) ) l=mid; 155 else r=mid; 156 } 157 printf("%d\n",l); 158 } 159 return 0; 160 }
11.(HDOJ3338)http://acm.hdu.edu.cn/showproblem.php?pid=3338
题意:对于每个黑格,如果黑格的左半部分有数代表该黑格以下的白格部分(到底或者到另一个黑格位置)之和为左半部分这个数值。同理可得右半部分代表往右。
分析:“行进列出”是关键,分为3部分,左边放黑格的左半部分,右边放黑格的右半部分,中间放白格。s和左半部分相连,容量为对应的黑格左半部分的值;t和右半部分相连,值为对应黑格右半部分的值。对于白格,分别和对应行和列的黑格所在的编号相连,容量都是9(因为所填数字最大是9),同时这时候要设置流量为1(因为最小为1)。但是该思路的代码TLE了....
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<vector> 5 #include<queue> 6 using namespace std; 7 const int maxn=150; 8 const int maxm=10050; 9 const int inf=1e9; 10 int line[maxn][maxn]; 11 int row[maxn][maxn],col[maxn][maxn],ans[maxn][maxn]; 12 int N,M; 13 int n,s,t; 14 struct edge{ 15 int from,to,cap,flow; 16 edge(int u,int v,int c,int f):from(u),to(v),cap(c),flow(f) {} 17 }; 18 vector<edge>E; 19 vector<int>G[maxm]; 20 bool vis[maxm]; 21 int d[maxm],cur[maxm]; 22 23 void init() 24 { 25 for ( int i=0;i<=n;i++ ) G[i].clear(); 26 E.clear(); 27 } 28 29 void addedge(int from,int to,int cap,int flow) 30 { 31 E.push_back(edge(from,to,cap,flow)); 32 E.push_back(edge(to,from,0,-flow)); 33 int m=E.size(); 34 G[from].push_back(m-2); 35 G[to].push_back(m-1); 36 } 37 38 bool BFS() 39 { 40 memset(vis,false,sizeof(vis)); 41 queue<int>que; 42 que.push(s); 43 d[s]=0; 44 vis[s]=true; 45 while ( !que.empty() ) { 46 int u=que.front(); 47 que.pop(); 48 for ( int i=0;i<G[u].size();i++ ) { 49 edge& e=E[G[u][i]]; 50 if ( !vis[e.to] && (e.cap-e.flow)>0 ) { 51 d[e.to]=d[u]+1; 52 vis[e.to]=true; 53 que.push(e.to); 54 } 55 } 56 } 57 return vis[t]; 58 } 59 60 int DFS(int u,int a) 61 { 62 if ( u==t || a==0 ) return a; 63 int flow=0,f; 64 for ( int& i=cur[u];i<G[u].size();i++ ) { 65 edge& e=E[G[u][i]]; 66 if ( d[u]+1==d[e.to] && (f=DFS(e.to,min(a,e.cap-e.flow)))>0 ) { 67 e.flow+=f; 68 E[G[u][i]^1].flow-=f; 69 flow+=f; 70 a-=f; 71 if ( a==0 ) break; 72 } 73 } 74 return flow; 75 } 76 77 int maxflow() 78 { 79 int flow=0; 80 while ( BFS() ) { 81 memset(cur,0,sizeof(cur)); 82 flow+=DFS(s,inf); 83 } 84 return flow; 85 } 86 87 int main() 88 { 89 char str[10]; 90 int i,j,k,x,y,z,num; 91 while ( scanf("%d%d",&N,&M)!=EOF ) { 92 memset(line,0,sizeof(line)); 93 memset(row,0,sizeof(row)); 94 memset(col,0,sizeof(col)); 95 memset(ans,0,sizeof(ans)); 96 for ( i=1;i<=N;i++ ) { 97 for ( j=1;j<=M;j++ ) { 98 scanf("%s",str); 99 if ( str[0]=='.' ) { 100 line[i][j]=2; 101 continue; 102 } 103 if ( str[0]=='X' && str[4]=='X' ) continue; 104 line[i][j]=1; 105 if ( str[0]!='X' ) { 106 x=0; 107 for ( k=0;k<3;k++ ) { 108 y=str[k]-'0'; 109 x=(x*10+y); 110 } 111 col[i][j]=x; 112 } 113 if ( str[4]!='X' ) { 114 x=0; 115 for ( k=4;k<7;k++ ) { 116 y=str[k]-'0'; 117 x=(x*10+y); 118 } 119 row[i][j]=x; 120 } 121 } 122 } 123 n=N*M*2+1; 124 s=0; 125 t=n; 126 init(); 127 for ( i=1;i<=N;i++ ) { 128 for ( j=1;j<=M;j++ ) { 129 if ( line[i][j]==1 ) { 130 if ( row[i][j]!=0 ) { 131 x=(i-1)*M+j; 132 num=0; 133 for ( k=j+1;k<=M;k++ ) { 134 y=(i-1)*M+k; 135 if ( line[i][k]==2 ) { 136 num++; 137 addedge(x,y,9,1); 138 } 139 else break; 140 } 141 addedge(s,x,row[i][j]-num,0); 142 } 143 if ( col[i][j]!=0 ) { 144 x=(i-1)*M+j+N*M; 145 num=0; 146 for ( k=i+1;k<=N;k++ ) { 147 y=(k-1)*M+j; 148 if ( line[k][j]==2 ) { 149 addedge(y,x,9,1); 150 num++; 151 } 152 else break; 153 } 154 addedge(x,t,col[i][j]-num,0); 155 } 156 } 157 } 158 } 159 maxflow(); 160 for ( i=1;i<=N;i++ ) { 161 for ( j=1;j<=M;j++ ) { 162 if ( line[i][j]==2 ) { 163 x=(i-1)*M+j; 164 ans[i][j]=E[G[x][0]].flow; 165 } 166 } 167 } 168 for ( i=1;i<=N;i++ ) { 169 for ( j=1;j<=M;j++ ) { 170 if ( j!=1 ) printf(" "); 171 if ( line[i][j]!=2 ) printf("_"); 172 else printf("%d",ans[i][j]); 173 } 174 printf("\n"); 175 } 176 } 177 return 0; 178 }
参考别人思路注意以下几点:1.对于至少为1,可以先令所有的最大容量都为8,最后所有格子都+1;
注意:不要把所有的点都编两个号会TLE的,只对有用的点编号.很麻烦,但要耐心
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<vector> 5 #include<queue> 6 using namespace std; 7 const int maxn=150; 8 const int maxm=20050; 9 const int inf=1e9; 10 int line[maxn][maxn]; 11 int row[maxn][maxn],col[maxn][maxn],ans[maxn][maxn],num[maxn][maxn]; 12 int N,M; 13 int n,s,t; 14 struct edge{ 15 int from,to,cap,flow; 16 edge(int u,int v,int c,int f):from(u),to(v),cap(c),flow(f) {} 17 }; 18 vector<edge>E; 19 vector<int>G[maxm]; 20 bool vis[maxm]; 21 int d[maxm],cur[maxm],cnt,cntr,cntc; 22 struct node{ 23 int x; 24 int y; 25 }arrr[maxm],arrc[maxm]; 26 27 void init() 28 { 29 for ( int i=0;i<=n;i++ ) G[i].clear(); 30 E.clear(); 31 } 32 33 void addedge(int from,int to,int cap) 34 { 35 E.push_back(edge(from,to,cap,0)); 36 E.push_back(edge(to,from,0,0)); 37 int m=E.size(); 38 G[from].push_back(m-2); 39 G[to].push_back(m-1); 40 } 41 42 bool BFS() 43 { 44 memset(vis,false,sizeof(vis)); 45 queue<int>que; 46 que.push(s); 47 d[s]=0; 48 vis[s]=true; 49 while ( !que.empty() ) { 50 int u=que.front(); 51 que.pop(); 52 for ( int i=0;i<G[u].size();i++ ) { 53 edge& e=E[G[u][i]]; 54 if ( !vis[e.to] && (e.cap-e.flow)>0 ) { 55 d[e.to]=d[u]+1; 56 vis[e.to]=true; 57 que.push(e.to); 58 } 59 } 60 } 61 return vis[t]; 62 } 63 64 int DFS(int u,int a) 65 { 66 if ( u==t || a==0 ) return a; 67 int flow=0,f; 68 for ( int& i=cur[u];i<G[u].size();i++ ) { 69 edge& e=E[G[u][i]]; 70 if ( d[u]+1==d[e.to] && (f=DFS(e.to,min(a,e.cap-e.flow)))>0 ) { 71 e.flow+=f; 72 E[G[u][i]^1].flow-=f; 73 flow+=f; 74 a-=f; 75 if ( a==0 ) break; 76 } 77 } 78 return flow; 79 } 80 81 int maxflow() 82 { 83 int flow=0; 84 while ( BFS() ) { 85 memset(cur,0,sizeof(cur)); 86 flow+=DFS(s,inf); 87 } 88 return flow; 89 } 90 91 int main() 92 { 93 char str[10]; 94 int i,j,k,x,y,z,num1,fx,fy,u,v; 95 while ( scanf("%d%d",&N,&M)!=EOF ) { 96 memset(line,0,sizeof(line)); 97 memset(row,0,sizeof(row)); 98 memset(col,0,sizeof(col)); 99 memset(ans,0,sizeof(ans)); 100 memset(num,0,sizeof(num)); 101 cnt=cntr=cntc=0; 102 for ( i=1;i<=N;i++ ) { 103 for ( j=1;j<=M;j++ ) { 104 scanf("%s",str); 105 if ( str[0]=='.' ) { 106 num[i][j]=++cnt; 107 line[i][j]=2; 108 continue; 109 } 110 if ( str[0]=='X' && str[4]=='X' ) continue; 111 line[i][j]=1; 112 if ( str[0]!='X' ) { 113 x=0; 114 for ( k=0;k<3;k++ ) { 115 y=str[k]-'0'; 116 x=(x*10+y); 117 } 118 col[i][j]=x; 119 arrc[++cntc].x=i; 120 arrc[cntc].y=j; 121 } 122 if ( str[4]!='X' ) { 123 x=0; 124 for ( k=4;k<7;k++ ) { 125 y=str[k]-'0'; 126 x=(x*10+y); 127 } 128 row[i][j]=x; 129 arrr[++cntr].x=i; 130 arrr[cntr].y=j; 131 } 132 } 133 } 134 n=cnt+cntr+cntc+1; 135 s=0; 136 t=n; 137 init(); 138 for ( i=1;i<=cntr;i++ ) { 139 x=arrr[i].x; 140 y=arrr[i].y; 141 u=i; 142 num1=0; 143 for ( k=y+1;k<=M;k++ ) { 144 if ( line[x][k]==2 ) { 145 v=num[x][k]+cntr; 146 num1++; 147 addedge(u,v,8); 148 } 149 else break; 150 } 151 addedge(s,u,row[x][y]-num1); 152 } 153 for ( i=1;i<=cntc;i++ ) { 154 x=arrc[i].x; 155 y=arrc[i].y; 156 u=i+cnt+cntr; 157 num1=0; 158 for ( k=x+1;k<=N;k++ ) { 159 if ( line[k][y]==2 ) { 160 v=num[k][y]+cntr; 161 num1++; 162 addedge(v,u,8); 163 } 164 else break; 165 } 166 addedge(u,t,col[x][y]-num1); 167 } 168 maxflow(); 169 for ( i=1;i<=N;i++ ) { 170 for ( j=1;j<=M;j++ ) { 171 if ( line[i][j]==2 ) { 172 x=cntr+num[i][j]; 173 ans[i][j]=-E[G[x][0]].flow; 174 } 175 } 176 } 177 for ( i=1;i<=N;i++ ) { 178 for ( j=1;j<=M;j++ ) { 179 if ( j!=1 ) printf(" "); 180 if ( line[i][j]!=2 ) printf("_"); 181 else printf("%d",ans[i][j]+1); 182 } 183 printf("\n"); 184 } 185 } 186 return 0; 187 }
12.(HDOJ2485)http://acm.hdu.edu.cn/showproblem.php?pid=2485
两种思路:最小割+费用流和最小割+最大流(常见的错误思路)。
A.最小割+最大流(常见的错误思路):该题大致同(HDOJ4289)去点使得起点和终点不可到达,为最小割的模型(对[2,n-1]的点进行拆点,建容量为1的边)。但是对于处理当最短路<=k的情况,这时候对于两点u,v来说当dis[s][u]+dis[v][t]+d(u,v)<k时才需要加入最大流中,这是本题的关键所在。这样的做法是错误的,只有刚好在最短路上的边才能添加进最大流中进行计算,否则会出现错误。如下面一个样例:
8 10 5
1 2
2 3
3 4
4 5
5 6
6 8
1 7
7 8
4 7
7 4
正确答案应该是1(去掉第7个点),而如果用这种方法会算出2。
B.费用流。边的容量和上面相同,此时设置费用,对起点和终点以外的其他端点拆点后的边费用设为1,其他所有边的费用均为0。当总费用>k时结束算法。
注意:1.对于某个点既要考虑到起点又要考虑到终点的距离,同时数据又不大时,先想到用floyd。
2.将所有边保存下来,最后逐一访问看边和其两端的点是否满足条件
3.注意区别最短路中的N和最大流中的n
4.有关图论的题目可以自己画图手动模拟一下
5.对于i,j两点的边容量大小没有关系,并不会影响到最后的结果。可以设置超级源点和汇点,也可以把1和2*N当作源点和汇点都没有关系。
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<queue> 5 #include<vector> 6 using namespace std; 7 const int maxn=305; 8 const int maxm=10050; 9 const int inf=1e9; 10 struct edge{ 11 int from,to,flow,cost; 12 edge(int u,int v,int f,int c):from(u),to(v),flow(f),cost(c) {} 13 }; 14 struct node{ 15 int x; 16 int y; 17 }arr[maxm]; 18 int n,s,t,m; 19 long long cost; 20 vector<edge>E; 21 vector<int>G[maxn]; 22 bool vis[maxn]; 23 int d[maxn],p[maxn],a[maxn]; 24 25 void init() 26 { 27 for ( int i=0;i<=n;i++ ) G[i].clear(); 28 E.clear(); 29 } 30 31 void addedge(int u,int v,int f,int c) 32 { 33 E.push_back(edge(u,v,f,c)); 34 E.push_back(edge(v,u,0,-c)); 35 int m=E.size(); 36 G[u].push_back(m-2); 37 G[v].push_back(m-1); 38 } 39 40 bool bellmanford(int& flow) 41 { 42 for ( int i=0;i<=n;i++ ) d[i]=inf; 43 memset(vis,false,sizeof(vis)); 44 d[s]=0; 45 vis[s]=true; 46 p[s]=0; 47 a[s]=inf; 48 queue<int>que; 49 que.push(s); 50 while ( !que.empty() ) { 51 int u=que.front(); 52 que.pop(); 53 vis[u]=false; 54 for ( int i=0;i<G[u].size();i++ ) { 55 edge& e=E[G[u][i]]; 56 if ( e.flow>0 && d[e.to]>d[u]+e.cost ) { 57 d[e.to]=d[u]+e.cost; 58 p[e.to]=G[u][i]; 59 a[e.to]=min(a[u],e.flow); 60 if ( !vis[e.to] ) { 61 que.push(e.to); 62 vis[e.to]=true; 63 } 64 } 65 } 66 } 67 if ( d[t]==inf ) return false; 68 cost=(long long)d[t]*(long long)a[t]; 69 if( cost>m ) return false; 70 flow+=a[t]; 71 for ( int u=t;u!=s;u=E[p[u]].from ) { 72 E[p[u]].flow-=flow; 73 E[p[u]^1].flow+=flow; 74 } 75 return true; 76 } 77 78 int mincost() 79 { 80 int flow=0; 81 cost=0; 82 while ( bellmanford(flow)); 83 return flow; 84 } 85 86 int main() 87 { 88 int N,i,j,k,x,y,z,M,ans; 89 while ( scanf("%d%d%d",&N,&M,&m)!=EOF && (N+M+m) ) { 90 s=0; 91 n=2*N+1; 92 t=n; 93 init(); 94 for ( i=0;i<M;i++ ) { 95 scanf("%d%d",&x,&y); 96 arr[i].x=x; 97 arr[i].y=y; 98 addedge(x+N,y,1,1); 99 } 100 addedge(s,1,inf,0); 101 addedge(2*N,t,inf,0); 102 addedge(1,1+N,inf,0); 103 addedge(N,2*N,inf,0); 104 for ( i=2;i<N;i++ ) addedge(i,i+N,1,0); 105 ans=mincost(); 106 printf("%d\n",ans); 107 } 108 return 0; 109 }