专题训练之费用流
基于BellmanFord求最短路的最小费用流模板
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 inf=1e9; 9 struct edge{ 10 int from,to,flow,cost; 11 edge(int u,int v,int f,int c):from(u),to(v),flow(f),cost(c) {} 12 }; 13 int n,s,t; 14 long long cost; 15 vector<edge>E; 16 vector<int>G[maxn]; 17 bool vis[maxn]; 18 int d[maxn],p[maxn],a[maxn]; //d为最短距离(相当于最少费用),p用于存储该点的前一条边,a用于求流量 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 u,int v,int f,int c) 27 { 28 E.push_back(edge(u,v,f,c)); 29 E.push_back(edge(v,u,0,-c)); 30 int m=E.size(); 31 G[u].push_back(m-2); 32 G[v].push_back(m-1); 33 } 34 35 bool bellmanford(int& flow) 36 { 37 for ( int i=0;i<=n;i++ ) d[i]=inf; 38 memset(vis,false,sizeof(vis)); 39 d[s]=0; 40 vis[s]=true; 41 p[s]=0; 42 a[s]=inf; 43 queue<int>que; 44 que.push(s); 45 while ( !que.empty() ) { 46 int u=que.front(); 47 que.pop(); 48 vis[u]=false; 49 for ( int i=0;i<G[u].size();i++ ) { 50 edge& e=E[G[u][i]]; 51 if ( e.flow>0 && d[e.to]>d[u]+e.cost ) { 52 d[e.to]=d[u]+e.cost; 53 p[e.to]=G[u][i]; 54 a[e.to]=min(a[u],e.flow); 55 if ( !vis[e.to] ) { 56 que.push(e.to); 57 vis[e.to]=true; 58 } 59 } 60 } 61 } 62 if ( d[t]==inf ) return false; 63 flow+=a[t]; 64 cost+=(long long)d[t]*(long long)a[t]; 65 for ( int u=t;u!=s;u=E[p[u]].from ) { 66 E[p[u]].flow-=a[t]; 67 E[p[u]^1].flow+=a[t]; 68 } 69 return true; 70 } 71 72 //需要保证初始网络种没有负权图 73 int mincost() 74 { 75 int flow=0; 76 cost=0; 77 while ( bellmanford(flow)); 78 return flow; 79 } 80 81 最小费用流
几道例题
1.(POJ2135)http://poj.org/problem?id=2135
题意:给定一定无向图,求从起到到终点,再从终点到起点的最短路。每条边走过以后不能再走。
分析:因为每条边只能走一次,所以如果采用最短路先求出正向的最短路然后把正向走过的边删去,再走反向,则最终的答案不一定是最短的。正确的做法是考虑从起点走两次到终点。
思考:A.如何控制流量为某个值(此题是2,因为要从起点到终点2次)?正确的做法是设置源点使源点到1的容量为那个要求值(此题是2),而不是更改费用流模板中的数值(要正确理解模板中某个变量,每个值赋那个值的意义、含义).
接A:为什么不能执行两次bellmanford?两次bellmanford不是也相当于流量为2么?
B..关于最小费用流的无向图?直接addedge(u,v.....)和addedge(v,u.....)将填边操作执行两次(增加了4条边)
C.正向边补的反向边和原有反向边本身的差别(为什么不能只存在两个原有边,互为反向边)?如何保证当一条边走了一次后另一个方向一定不会再次经过?因为走过一条边,再走另一条边,等于将原本边的流量退下来
反向边(费用为负)的作用是为了“回溯”/“反悔”,后悔之前的操作,将之前的操作取消,将花掉的前拿回来。
假设在一个图中一条无向边两端都有流量流入,那么一定不会是最小费用(不如不走这条无向边,还节省了费用)。
类似于乘公交车,从A地乘车到B地和B地乘车到A地都需要x元,如果既坐过去又坐回来,相当于没坐车但是却花费了2*x;而反向边的作用是时光倒流???直接不坐,省掉花费。
所以在一个图中,一条反向边要么走一边,要么两边都不走
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 inf=1e9; 9 struct edge{ 10 int from,to,flow,cost; 11 edge(int u,int v,int f,int c):from(u),to(v),flow(f),cost(c) {} 12 }; 13 int n,s,t; 14 long long cost; 15 vector<edge>E; 16 vector<int>G[maxn]; 17 bool vis[maxn]; 18 int d[maxn],p[maxn],a[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 u,int v,int f,int c) 27 { 28 E.push_back(edge(u,v,f,c)); 29 E.push_back(edge(v,u,0,-c)); 30 int m=E.size(); 31 G[u].push_back(m-2); 32 G[v].push_back(m-1); 33 } 34 35 bool bellmanford(int& flow) 36 { 37 for ( int i=0;i<=n;i++ ) d[i]=inf; 38 memset(vis,false,sizeof(vis)); 39 d[s]=0; 40 vis[s]=true; 41 p[s]=0; 42 a[s]=inf; 43 queue<int>que; 44 que.push(s); 45 while ( !que.empty() ) { 46 int u=que.front(); 47 que.pop(); 48 vis[u]=false; 49 for ( int i=0;i<G[u].size();i++ ) { 50 edge& e=E[G[u][i]]; 51 if ( e.flow>0 && d[e.to]>d[u]+e.cost ) { 52 d[e.to]=d[u]+e.cost; 53 p[e.to]=G[u][i]; 54 a[e.to]=min(a[u],e.flow); 55 if ( !vis[e.to] ) { 56 que.push(e.to); 57 vis[e.to]=true; 58 } 59 } 60 } 61 } 62 if ( d[t]==inf ) return false; 63 flow-=a[t]; 64 cost+=(long long)d[t]*(long long)a[t]; 65 for ( int u=t;u!=s;u=E[p[u]].from ) { 66 E[p[u]].flow-=a[t]; 67 E[p[u]^1].flow+=a[t]; 68 } 69 return true; 70 } 71 72 int mincost() 73 { 74 int flow=0; 75 cost=0; 76 while ( bellmanford(flow)); 77 return flow; 78 } 79 80 int main() 81 { 82 int m,i,j,k,x,y,z,ans,N; 83 while ( scanf("%d%d",&n,&m)!=EOF ) { 84 s=0; 85 t=n; 86 init(); 87 while ( m-- ) { 88 scanf("%d%d%d",&x,&y,&z); 89 addedge(x,y,1,z); 90 addedge(y,x,1,z); 91 } 92 addedge(s,1,2,0); 93 mincost(); 94 printf("%lld\n",cost); 95 } 96 return 0; 97 }
2.(POJ2175)http://poj.org/problem?id=2175
题意:给定N个大楼和M个避难所,给定每个大楼的坐标和人数,给定每个避难所所能承受的最大人数限制,再分配每栋大楼向每个避难所的人数,求该分配方案是否为时间最小。如果不是最小则输出一组时间比它更小的一组
分析:很直白的费用流,s到大楼建容量为所在的人数费用为0的边,避难所到t建容量为避难所所能承受的最大人数费用为0的边,大楼到避难所建容量为inf(也可以是两边人数中较小的那个)费用为距离的边。跑费用流比较产生的费用和原先所分配的费用。但是此方法会TLE
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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=305; 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 int n,s,t; 15 long long cost; 16 vector<edge>E; 17 vector<int>G[maxn]; 18 bool vis[maxn]; 19 int d[maxn],p[maxn],a[maxn],mp[maxn][maxn],mp2[maxn][maxn]; 20 struct node{ 21 int x; 22 int y; 23 int p; 24 }arr1[maxn],arr2[maxn]; 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 u,int v,int f,int c) 33 { 34 E.push_back(edge(u,v,f,c)); 35 E.push_back(edge(v,u,0,-c)); 36 int m=E.size(); 37 G[u].push_back(m-2); 38 G[v].push_back(m-1); 39 } 40 41 bool bellmanford(int& flow) 42 { 43 for ( int i=0;i<=n;i++ ) d[i]=inf; 44 memset(vis,false,sizeof(vis)); 45 d[s]=0; 46 vis[s]=true; 47 p[s]=0; 48 a[s]=inf; 49 queue<int>que; 50 que.push(s); 51 while ( !que.empty() ) { 52 int u=que.front(); 53 que.pop(); 54 vis[u]=false; 55 for ( int i=0;i<G[u].size();i++ ) { 56 edge& e=E[G[u][i]]; 57 if ( e.flow>0 && d[e.to]>d[u]+e.cost ) { 58 d[e.to]=d[u]+e.cost; 59 p[e.to]=G[u][i]; 60 a[e.to]=min(a[u],e.flow); 61 if ( !vis[e.to] ) { 62 que.push(e.to); 63 vis[e.to]=true; 64 } 65 } 66 } 67 } 68 if ( d[t]==inf ) return false; 69 flow+=a[t]; 70 cost+=(long long)d[t]*(long long)a[t]; 71 for ( int u=t;u!=s;u=E[p[u]].from ) { 72 E[p[u]].flow-=a[t]; 73 E[p[u]^1].flow+=a[t]; 74 } 75 return true; 76 } 77 78 int dist(node a,node b) 79 { 80 return abs(a.x-b.x)+abs(a.y-b.y)+1; 81 } 82 83 void mincost() 84 { 85 int flow=0; 86 cost=0; 87 while ( bellmanford(flow)); 88 return; 89 } 90 91 int main() 92 { 93 int N,M,i,j,k,x,y,z,d,sum; 94 while ( scanf("%d%d",&N,&M)!=EOF ) { 95 s=0; 96 n=N+M+1; 97 t=n; 98 init(); 99 for ( i=1;i<=N;i++ ) { 100 scanf("%d%d%d",&arr1[i].x,&arr1[i].y,&arr1[i].p); 101 addedge(s,i,arr1[i].p,0); 102 } 103 for ( i=1;i<=M;i++ ) { 104 scanf("%d%d%d",&arr2[i].x,&arr2[i].y,&arr2[i].p); 105 addedge(i+N,t,arr2[i].p,0); 106 } 107 for ( i=1;i<=N;i++ ) { 108 for ( j=1;j<=M;j++ ) scanf("%d",&mp[i][j]); 109 } 110 sum=0; 111 for ( i=1;i<=N;i++ ) { 112 for ( j=1;j<=M;j++ ) { 113 d=dist(arr1[i],arr2[j]); 114 sum+=d*mp[i][j]; 115 addedge(i,j+N,inf,d); 116 } 117 } 118 mincost(); 119 if ( sum==cost ) printf("OPTIMAL\n"); 120 else { 121 printf("SUBOPTIMAL\n"); 122 for ( i=1;i<=N;i++ ) { 123 for ( j=1;j<=M;j++ ) { 124 mp2[i][j]=inf-E[G[i][j]].flow; 125 if ( j!=1 ) printf(" "); 126 printf("%d",mp2[i][j]); 127 } 128 printf("\n"); 129 } 130 } 131 } 132 return 0; 133 }
此题要用消负圈算法(只用求比给定的小)。具体就是根据残量网络跑最短路,检查有无负圈。如果有则沿着负圈把流量+1(同时反向边-1)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<cmath> 5 using namespace std; 6 const int maxn=205; 7 const int inf=1e9; 8 int N,M,s,t,n; 9 int X[maxn],Y[maxn],B[maxn]; 10 int P[maxn],Q[maxn],C[maxn]; 11 int E[maxn][maxn]; 12 int g[maxn][maxn]; 13 int pre[maxn][maxn]; 14 bool vis[maxn]; 15 16 int main() 17 { 18 int i,j,k,x,y,z,h; 19 bool flag; 20 while ( scanf("%d%d",&N,&M)!=EOF ) { 21 flag=false; 22 for ( i=1;i<=N;i++ ) scanf("%d%d%d",&X[i],&Y[i],&B[i]); 23 for ( i=1;i<=M;i++ ) scanf("%d%d%d",&P[i],&Q[i],&C[i]); 24 for ( i=1;i<=N;i++ ) { 25 for ( j=1;j<=M;j++ ) scanf("%d",&E[i][j]); 26 } 27 n=N+M+1; 28 t=n; 29 for ( i=0;i<=n;i++ ) { 30 for ( j=0;j<=n;j++ ) g[i][j]=inf; 31 } 32 for ( j=1;j<=M;j++ ) { 33 int sum=0; 34 for ( i=1;i<=N;i++ ) { 35 int c=abs(X[i]-P[j])+abs(Y[i]-Q[j])+1; 36 g[i][j+N]=c; 37 if ( E[i][j]>0 ) g[j+N][i]=-c; 38 sum+=E[i][j]; 39 } 40 if ( sum>0 ) g[n][j+N]=0; 41 if ( sum<C[j] ) g[j+N][n]=0; 42 } 43 for ( i=1;i<=n;i++ ) { 44 for ( j=1;j<=n;j++ ) pre[i][j]=i; 45 } 46 for ( k=1;k<=n;k++ ) { 47 for ( i=1;i<=n;i++ ) { 48 for ( j=1;j<=n;j++ ) { 49 if ( g[i][j]>g[i][k]+g[k][j] ) { 50 g[i][j]=g[i][k]+g[k][j]; 51 pre[i][j]=pre[k][j]; 52 if ( i==j && g[i][i]<0 ) { 53 for ( h=1;h<=n;h++ ) vis[h]=false; 54 for ( int v=i;!vis[v];v=pre[i][v] ) { 55 vis[v]=true; 56 if ( v!=n && pre[i][v]!=n ) { 57 if ( v>N ) E[pre[i][v]][v-N]++; 58 else E[v][pre[i][v]-N]--; 59 } 60 } 61 printf("SUBOPTIMAL\n"); 62 for ( x=1;x<=N;x++ ) { 63 for ( y=1;y<=M;y++ ) { 64 printf("%d",E[x][y]); 65 if ( y!=M ) printf(" "); 66 else printf("\n"); 67 } 68 } 69 flag=true; 70 break; 71 } 72 } 73 if ( flag ) break; 74 } 75 if ( flag ) break; 76 } 77 if ( flag ) break; 78 } 79 if ( !flag ) printf("OPTIMAL\n"); 80 } 81 return 0; 82 }
需要对残量网络和最短路种处理负圈很清楚才能容易理解。
残量网络:
最短路处理负圈:
3.(POJ3686)http://poj.org/problem?id=3686
题意:有n个玩具和m个工厂,给出每个玩具在每个工厂制作所需要的时间,一个工厂不能同时做多个玩具,求需要花费的最少时间
分析:(从简单的地方出发)如果只有一个工厂,那么对于所有玩具则是T=(t1)+(t1+t2)+......(t1+t2+...+tn)=n*t1+(n-1)*t2+...tn,易得到当ti<=t2<=t3<=.....<=tn时,T取到最小(即对于一个工厂我们可以采用贪心的算法,每次取当前耗时最少的玩具)。而当有多个工厂时,我们无法确定哪个玩具在哪个工厂制造,也就没办法将问题分解为一个个工厂然后每个工厂各自贪心的形式。这时候回顾以上式子可以发现可以将其理解为n个只能制造一个玩具的工厂(即将一个工厂拆分成n个,工厂i制造这个玩具需要花费i*ti的时间)。如当有流量从第i个玩具(的编号)流向第j个工厂的第k个部分(的编号)的时候,相当于玩具i是在工厂j制作的,而且是所有在第j个工厂中倒数第k个制作的
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<queue> 5 #include<vector> 6 using namespace std; 7 const int maxn=3005; 8 const int inf=1e9; 9 struct edge{ 10 int from,to,flow,cost; 11 edge(int u,int v,int f,int c):from(u),to(v),flow(f),cost(c) {} 12 }; 13 int n,s,t; 14 long long cost; 15 vector<edge>E; 16 vector<int>G[maxn]; 17 bool vis[maxn]; 18 int d[maxn],p[maxn],a[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 u,int v,int f,int c) 27 { 28 E.push_back(edge(u,v,f,c)); 29 E.push_back(edge(v,u,0,-c)); 30 int m=E.size(); 31 G[u].push_back(m-2); 32 G[v].push_back(m-1); 33 } 34 35 bool bellmanford(int& flow) 36 { 37 for ( int i=0;i<=n;i++ ) d[i]=inf; 38 memset(vis,false,sizeof(vis)); 39 d[s]=0; 40 vis[s]=true; 41 p[s]=0; 42 a[s]=inf; 43 queue<int>que; 44 que.push(s); 45 while ( !que.empty() ) { 46 int u=que.front(); 47 que.pop(); 48 vis[u]=false; 49 for ( int i=0;i<G[u].size();i++ ) { 50 edge& e=E[G[u][i]]; 51 if ( e.flow>0 && d[e.to]>d[u]+e.cost ) { 52 d[e.to]=d[u]+e.cost; 53 p[e.to]=G[u][i]; 54 a[e.to]=min(a[u],e.flow); 55 if ( !vis[e.to] ) { 56 que.push(e.to); 57 vis[e.to]=true; 58 } 59 } 60 } 61 } 62 if ( d[t]==inf ) return false; 63 flow+=a[t]; 64 cost+=(long long)d[t]*(long long)a[t]; 65 for ( int u=t;u!=s;u=E[p[u]].from ) { 66 E[p[u]].flow-=a[t]; 67 E[p[u]^1].flow+=a[t]; 68 } 69 return true; 70 } 71 72 int mincost() 73 { 74 int flow=0; 75 cost=0; 76 while ( bellmanford(flow)); 77 return flow; 78 } 79 80 int main() 81 { 82 int T,i,j,k,N,M,x,y,z,u,v; 83 scanf("%d",&T); 84 while ( T-- ) { 85 scanf("%d%d",&N,&M); 86 s=0; 87 n=N+N*M+1; 88 t=n; 89 init(); 90 for ( i=1;i<=N;i++ ) { 91 addedge(s,i,1,0); 92 for ( j=1;j<=M;j++ ) { 93 scanf("%d",&x); 94 for ( k=1;k<=N;k++ ) { 95 y=x*k; 96 u=j*N+k; 97 addedge(i,u,1,y); 98 } 99 } 100 } 101 for ( i=1;i<=M;i++ ) { 102 for ( j=1;j<=N;j++ ) { 103 u=i*N+j; 104 addedge(u,t,1,0); 105 } 106 } 107 mincost(); 108 printf("%.6lf\n",(double)cost/N); 109 } 110 return 0; 111 }
注意:最后需要输出平均值即(double)cost/N
小结:A.最大流/最小费用流与最短路结合的题目,往往会限制边,使得一条边只能走一次,这就代表着边的容量为1.而距离则用最小费用流中的花费所替代。最终的流量即为从起点到终点的次数,而花费则为从起点到终点的距离
B.最小费用最大流的题目往往会涉及得到什么(流量)/消耗了什么(费用),得到了什么(做一件事情的收获 )可以是人的转移,物品的生产;消耗了什么(做一件事情花费的代价)可以是时间的消耗,钱的消耗,距离的消耗 。
练习题
1.(HDOJ1533) http://acm.hdu.edu.cn/showproblem.php?pid=1533
入门题,设置超级源点和超级汇点,源点与所有的人相连(费用为0,流量为1),房子和所有的汇点相连(费用为0,流量为1),将每个人和所有房子的曼哈顿距离求出建边(费用为距离,流量为1).
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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=205; 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 int n,s,t; 15 long long cost; 16 vector<edge>E; 17 vector<int>G[maxn]; 18 bool vis[maxn]; 19 int d[maxn],p[maxn],a[maxn]; 20 struct node{ 21 int x; 22 int y; 23 }arr1[maxn],arr2[maxn]; 24 char mp[maxn][maxn]; 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 u,int v,int f,int c) 33 { 34 E.push_back(edge(u,v,f,c)); 35 E.push_back(edge(v,u,0,-c)); 36 int m=E.size(); 37 G[u].push_back(m-2); 38 G[v].push_back(m-1); 39 } 40 41 bool bellmanford(int& flow) 42 { 43 for ( int i=0;i<=n;i++ ) d[i]=inf; 44 memset(vis,false,sizeof(vis)); 45 d[s]=0; 46 vis[s]=true; 47 p[s]=0; 48 a[s]=inf; 49 queue<int>que; 50 que.push(s); 51 while ( !que.empty() ) { 52 int u=que.front(); 53 que.pop(); 54 vis[u]=false; 55 for ( int i=0;i<G[u].size();i++ ) { 56 edge& e=E[G[u][i]]; 57 if ( e.flow>0 && d[e.to]>d[u]+e.cost ) { 58 d[e.to]=d[u]+e.cost; 59 p[e.to]=G[u][i]; 60 a[e.to]=min(a[u],e.flow); 61 if ( !vis[e.to] ) { 62 que.push(e.to); 63 vis[e.to]=true; 64 } 65 } 66 } 67 } 68 if ( d[t]==inf ) return false; 69 flow+=a[t]; 70 cost+=(long long)d[t]*(long long)a[t]; 71 for ( int u=t;u!=s;u=E[p[u]].from ) { 72 E[p[u]].flow-=a[t]; 73 E[p[u]^1].flow+=a[t]; 74 } 75 return true; 76 } 77 78 int dist(node a,node b) 79 { 80 return abs(a.x-b.x)+abs(a.y-b.y); 81 } 82 83 void mincost() 84 { 85 int flow=0; 86 cost=0; 87 while ( bellmanford(flow)); 88 return; 89 } 90 91 int main() 92 { 93 int N,M,i,j,k,x,y,cnt1,cnt2,d; 94 while ( scanf("%d%d",&N,&M)!=EOF && (N+M) ) { 95 for ( i=1;i<=N;i++ ) scanf("%s",mp[i]+1); 96 cnt1=cnt2=0; 97 for ( i=1;i<=N;i++ ) { 98 for ( j=1;j<=M;j++ ) { 99 if ( mp[i][j]=='m' ) { 100 arr1[++cnt1].x=i; 101 arr1[cnt1].y=j; 102 } 103 else if ( mp[i][j]=='H' ) { 104 arr2[++cnt2].x=i; 105 arr2[cnt2].y=j; 106 } 107 } 108 } 109 n=cnt1+cnt2+1; 110 s=0; 111 t=n; 112 init(); 113 for ( i=1;i<=cnt1;i++ ) { 114 addedge(s,i,1,0); 115 addedge(i+cnt1,t,1,0); 116 } 117 for ( i=1;i<=cnt1;i++ ) { 118 for ( j=1;j<=cnt2;j++ ) { 119 d=dist(arr1[i],arr2[j]); 120 addedge(i,j+cnt1,1,d); 121 } 122 } 123 mincost(); 124 printf("%lld\n",cost); 125 } 126 return 0; 127 }
2.(HDOJ3667)http://acm.hdu.edu.cn/showproblem.php?pid=3667
题意:给定n,m,k,分别表示城市数,单向道路数和需要运送的货物重量,其中起点为1,终点为n。对于每条路给出ui,vi,ai,ci。表示从ui到vi的道路上危险系数为ai,该条路最多运输ci的物资。危险系数定义为在危险系数为ai的路上运送重量为x的货物需要花费ai*x*x的钱,问将全部货物从起点送到终点,最少需要花费多少钱。
分析:参照大白书上的做法,对于费用和容量的平方成正比,一般采用的是拆边的方法,将边拆为容量都为1,但是费用分别为1,3,5,7...(2*n-1)这样的奇数(奇数数列的求和公式为n*n,刚好满足题目的要求)。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 inf=1e9; 9 struct edge{ 10 int from,to,flow,cost; 11 edge(int u,int v,int f,int c):from(u),to(v),flow(f),cost(c) {} 12 }; 13 int n,s,t; 14 long long cost; 15 vector<edge>E; 16 vector<int>G[maxn]; 17 bool vis[maxn]; 18 int d[maxn],p[maxn],a[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 u,int v,int f,int c) 27 { 28 E.push_back(edge(u,v,f,c)); 29 E.push_back(edge(v,u,0,-c)); 30 int m=E.size(); 31 G[u].push_back(m-2); 32 G[v].push_back(m-1); 33 } 34 35 bool bellmanford(int& flow) 36 { 37 for ( int i=0;i<=n;i++ ) d[i]=inf; 38 memset(vis,false,sizeof(vis)); 39 d[s]=0; 40 vis[s]=true; 41 p[s]=0; 42 a[s]=inf; 43 queue<int>que; 44 que.push(s); 45 while ( !que.empty() ) { 46 int u=que.front(); 47 que.pop(); 48 vis[u]=false; 49 for ( int i=0;i<G[u].size();i++ ) { 50 edge& e=E[G[u][i]]; 51 if ( e.flow>0 && d[e.to]>d[u]+e.cost ) { 52 d[e.to]=d[u]+e.cost; 53 p[e.to]=G[u][i]; 54 a[e.to]=min(a[u],e.flow); 55 if ( !vis[e.to] ) { 56 que.push(e.to); 57 vis[e.to]=true; 58 } 59 } 60 } 61 } 62 if ( d[t]==inf ) return false; 63 flow+=a[t]; 64 cost+=(long long)d[t]*(long long)a[t]; 65 for ( int u=t;u!=s;u=E[p[u]].from ) { 66 E[p[u]].flow-=a[t]; 67 E[p[u]^1].flow+=a[t]; 68 } 69 return true; 70 } 71 72 int mincost() 73 { 74 int flow=0; 75 cost=0; 76 while ( bellmanford(flow)); 77 return flow; 78 } 79 80 int main() 81 { 82 int N,M,K,i,j,k,x,y,z,ans,sum,u,v; 83 while ( scanf("%d%d%d",&N,&M,&K)!=EOF ) { 84 s=0; 85 n=N+1; 86 t=n; 87 init(); 88 addedge(s,1,K,0); 89 addedge(N,n,K,0); 90 while ( M-- ) { 91 scanf("%d%d%d%d",&u,&v,&x,&y); 92 for ( i=1;i<=y;i++ ) { 93 addedge(u,v,1,(2*i-1)*x); 94 } 95 } 96 ans=mincost(); 97 if ( ans<K ) printf("-1\n"); 98 else printf("%lld\n",cost); 99 } 100 return 0; 101 }
3.(HDOJ4067)http://acm.hdu.edu.cn/showproblem.php?pid=4067
题意:给定n,m,s,t,分别表示n个点,m条边,起点为s,终点为t。对于每条边都有u,v,a,b,表示从u到v的边去掉需要花费b,保留需要花费a。最后要求最终的图满足五个条件:所有道路必须是单向的,只有一个起点和终点,起点的出度=入度+1,终点的入度=出度+1.,除去起点和终点其他点的入度和出度相等。求最少的花费
分析:思考一下几个问题:A.如何判断哪些边是要的,哪些边是不要的(费用怎么算)?
B.如何符合题目中所给的条件,并且如何检查是否满足条件?
4.(HDOJ3315)http://acm.hdu.edu.cn/showproblem.php?pid=3315
题意:有两个人每个人都有N个怪物,每个宠物都有自己的血量和攻击力,对于第一个人的i个怪来说,只要获胜便可以增加Vi的分数,但如果失败要扣除Vi的分数,现在第一个人可以该边怪物的对战顺序。求最后所有怪物都比赛完后第一个获胜能获得的最大分数,并且求出排列顺序和原本顺序的相似度。
分析:分析题目可得题目所求为最大费用最大流,考虑建负边。对s和第一个人的怪物建容量为1,费用为0的边,对第二个人和t也建容量为1费用为0的边。而对第一个人的i和第二个人的j,如果i能够战胜j则建容量为1费用为-v[i]*(N+1)的负边,否则建v[i]*(N+1)的正边,若有(i==j)则让费用-1
注意审题:题目要求尽量不改变本来的顺序,那我们就需要让原本的边更容易被选择。但是如何才能让一条边更容易被选择呢?这时候就要从算法出发,算法每次都是优先选择费用较小的边进行操作,那么我们可以将原本的边费用减小1,为了不影响最终的结果,我们应该在最初的时候将费用扩大至少N+1倍,这样就算最后所有的边都是根据原有匹配流最小费用流的时候,最后结果ans=原本的结果-n,因为最后结果要/=N+1(所取的数),所以对最终的结果并不会有影响。而匹配度也可从ans%(N+1)得到(即偏差的量/减去了几个1,减去的1都代表和原有的匹配相同),或者对于匹配度的判断也可以每个点的边出发判断边的两端是否于原本匹配两端的点相同,同时残量网络为0。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 inf=1e9; 9 const double eps=1e-6; 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 int n,s,t; 15 long long cost; 16 vector<edge>E; 17 vector<int>G[maxn]; 18 bool vis[maxn],mp[maxn][maxn]; 19 int d[maxn],p[maxn],a[maxn],V[maxn]; 20 struct node{ 21 int hp; 22 int att; 23 }arr1[maxn],arr2[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 flow+=a[t]; 69 cost+=(long long)d[t]*(long long)a[t]; 70 for ( int u=t;u!=s;u=E[p[u]].from ) { 71 E[p[u]].flow-=a[t]; 72 E[p[u]^1].flow+=a[t]; 73 } 74 return true; 75 } 76 77 bool judge(node a,node b) 78 { 79 int t1,t2; //t1表示a死掉所需要的时间 80 if ( a.hp%b.att==0 ) t1=a.hp/b.att; 81 else t1=a.hp/b.att+1; 82 if ( b.hp%a.att==0 ) t2=b.hp/a.att; 83 else t2=b.hp/a.att+1; 84 if ( t1>=t2 ) return true; 85 return false; 86 } 87 88 int mincost() 89 { 90 int flow=0; 91 cost=0; 92 while ( bellmanford(flow)); 93 return flow; 94 } 95 96 int main() 97 { 98 int N,i,j,k,x,y,z,cnt; 99 double ans; 100 while ( scanf("%d",&N)!=EOF && N ) { 101 for ( i=1;i<=N;i++ ) scanf("%d",&V[i]); 102 s=0; 103 n=N*2+1; 104 t=n; 105 init(); 106 for ( i=1;i<=N;i++ ) scanf("%d",&arr1[i].hp); 107 for ( i=1;i<=N;i++ ) scanf("%d",&arr2[i].hp); 108 for ( i=1;i<=N;i++ ) scanf("%d",&arr1[i].att); 109 for ( i=1;i<=N;i++ ) scanf("%d",&arr2[i].att); 110 for ( i=1;i<=N;i++ ) { 111 for ( j=1;j<=N;j++ ) { 112 if ( judge(arr1[i],arr2[j]) ) { 113 if ( i!=j ) addedge(i,j+N,1,-V[i]*1000); 114 else addedge(i,j+N,1,-V[i]*1000-1); 115 } 116 else { 117 if ( i==j ) addedge(i,j+N,1,V[i]*1000-1); 118 else addedge(i,j+N,1,V[i]*1000); 119 } 120 } 121 } 122 for ( i=1;i<=N;i++ ) { 123 addedge(s,i,1,0); 124 addedge(i+N,t,1,0); 125 } 126 mincost(); 127 cost=-cost; 128 cnt=cost%1000; 129 cost/=1000; 130 if ( cost<=0 ) printf("Oh, I lose my dear seaco!\n"); 131 else { 132 ans=(double)100*cnt/N; 133 printf("%lld %.3lf%%\n",cost,ans); 134 } 135 } 136 return 0; 137 }
注意:最后输出%要printf("%%"),才能输出%,否则在本地能输出%,但是会WA,特别注意这种情况
特别要注意对两个人输赢的判断函数的写法
对于输入数据的顺序要格外注意
5.(HDOJ3395)http://acm.hdu.edu.cn/showproblem.php?pid=3395
题意:有n条鱼,每条鱼都有一个权值ai,给定n*n矩阵,(i,j)为1表示第i条鱼会攻击第j条鱼并产生ai XOR aj数量的卵,每条鱼只能攻击一条鱼和被一条鱼攻击求能产生最多卵的数量是多少。
分析:此题是求最大费用可行流(而不是最大费用最大流)。解决办法有两个,一个是只需要在每次跳出循环的时候判断下d[t]的值就可以了,另一个是对于每条攻击鱼要直接连一条边到终点,费用为0。
如上图,最大费用最大流则结果为3,而最大费用可行流则为12
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 inf=1e9; 9 struct edge{ 10 int from,to,flow,cost; 11 edge(int u,int v,int f,int c):from(u),to(v),flow(f),cost(c) {} 12 }; 13 int n,s,t; 14 long long cost; 15 vector<edge>E; 16 vector<int>G[maxn]; 17 bool vis[maxn]; 18 int d[maxn],p[maxn],a[maxn],V[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 u,int v,int f,int c) 28 { 29 E.push_back(edge(u,v,f,c)); 30 E.push_back(edge(v,u,0,-c)); 31 int m=E.size(); 32 G[u].push_back(m-2); 33 G[v].push_back(m-1); 34 } 35 36 bool bellmanford(int& flow) 37 { 38 for ( int i=0;i<=n;i++ ) d[i]=inf; 39 memset(vis,false,sizeof(vis)); 40 d[s]=0; 41 vis[s]=true; 42 p[s]=0; 43 a[s]=inf; 44 queue<int>que; 45 que.push(s); 46 while ( !que.empty() ) { 47 int u=que.front(); 48 que.pop(); 49 vis[u]=false; 50 for ( int i=0;i<G[u].size();i++ ) { 51 edge& e=E[G[u][i]]; 52 if ( e.flow>0 && d[e.to]>d[u]+e.cost ) { 53 d[e.to]=d[u]+e.cost; 54 p[e.to]=G[u][i]; 55 a[e.to]=min(a[u],e.flow); 56 if ( !vis[e.to] ) { 57 que.push(e.to); 58 vis[e.to]=true; 59 } 60 } 61 } 62 } 63 if ( d[t]==inf || d[t]>=0 ) return false; 64 flow+=a[t]; 65 cost+=(long long)d[t]*(long long)a[t]; 66 for ( int u=t;u!=s;u=E[p[u]].from ) { 67 E[p[u]].flow-=a[t]; 68 E[p[u]^1].flow+=a[t]; 69 } 70 return true; 71 } 72 73 int mincost() 74 { 75 int flow=0; 76 cost=0; 77 while ( bellmanford(flow)); 78 return flow; 79 } 80 81 int main() 82 { 83 int N,i,j,k,x,y,z; 84 while ( scanf("%d",&N)!=EOF && N ) { 85 for ( i=1;i<=N;i++ ) scanf("%d",&V[i]); 86 s=0; 87 n=2*N+1; 88 t=n; 89 init(); 90 for ( i=1;i<=N;i++ ) { 91 addedge(s,i,1,0); 92 addedge(i+N,t,1,0); 93 } 94 for ( i=1;i<=N;i++ ) scanf("%s",mp[i]+1); 95 for ( i=1;i<=N;i++ ) { 96 for ( j=1;j<=N;j++ ) { 97 if ( mp[i][j]=='1' ) addedge(i,j+N,1,-(V[i]^V[j])); 98 } 99 } 100 mincost(); 101 printf("%lld\n",-cost); 102 } 103 return 0; 104 }
6.(POJ2516)http://poj.org/problem?id=2516
题意:给定N,M,K,分别表示有N个店,M个仓库,K种商品。给出N行,每行有K个数代表第i个店需要第j个货物x件。给出M行,每行K个数,代表第i个仓库有第j类货物x件。再给出K个N*M矩阵,代表对于第i件货物来说,从商店j到仓库k需要的费用为x,现在求满足所有商店的需求所需要花费的最小费用
分析:第一想法是拆点,将每个店拆成K个点,将每个工厂拆成k个点,然后根据要求建边。但是TLE了
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<queue> 5 #include<vector> 6 using namespace std; 7 const int maxn=5005; 8 const int inf=1e9; 9 struct edge{ 10 int from,to,flow,cost; 11 edge(int u,int v,int f,int c):from(u),to(v),flow(f),cost(c) {} 12 }; 13 int n,s,t; 14 long long cost; 15 vector<edge>E; 16 vector<int>G[maxn]; 17 bool vis[maxn]; 18 int d[maxn],p[maxn],a[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 u,int v,int f,int c) 27 { 28 E.push_back(edge(u,v,f,c)); 29 E.push_back(edge(v,u,0,-c)); 30 int m=E.size(); 31 G[u].push_back(m-2); 32 G[v].push_back(m-1); 33 } 34 35 bool bellmanford(int& flow) 36 { 37 for ( int i=0;i<=n;i++ ) d[i]=inf; 38 memset(vis,false,sizeof(vis)); 39 d[s]=0; 40 vis[s]=true; 41 p[s]=0; 42 a[s]=inf; 43 queue<int>que; 44 que.push(s); 45 while ( !que.empty() ) { 46 int u=que.front(); 47 que.pop(); 48 vis[u]=false; 49 for ( int i=0;i<G[u].size();i++ ) { 50 edge& e=E[G[u][i]]; 51 if ( e.flow>0 && d[e.to]>d[u]+e.cost ) { 52 d[e.to]=d[u]+e.cost; 53 p[e.to]=G[u][i]; 54 a[e.to]=min(a[u],e.flow); 55 if ( !vis[e.to] ) { 56 que.push(e.to); 57 vis[e.to]=true; 58 } 59 } 60 } 61 } 62 if ( d[t]==inf ) return false; 63 flow+=a[t]; 64 cost+=(long long)d[t]*(long long)a[t]; 65 for ( int u=t;u!=s;u=E[p[u]].from ) { 66 E[p[u]].flow-=a[t]; 67 E[p[u]^1].flow+=a[t]; 68 } 69 return true; 70 } 71 72 int mincost() 73 { 74 int flow=0; 75 cost=0; 76 while ( bellmanford(flow)); 77 return flow; 78 } 79 80 int main() 81 { 82 int N,M,K,i,j,k,x,y,z,u,v,sum,ans; 83 while ( scanf("%d%d%d",&N,&M,&K)!=EOF && (N+M+K) ) { 84 sum=0; 85 s=0; 86 n=N*K+M*K+1; 87 t=n; 88 init(); 89 for ( i=1;i<=N;i++ ) { 90 for ( j=1;j<=K;j++ ) { 91 scanf("%d",&x); 92 u=(i-1)*K+j; 93 addedge(s,u,x,0); 94 sum+=x; 95 } 96 } 97 for ( i=1;i<=M;i++ ) { 98 for ( j=1;j<=K;j++ ) { 99 scanf("%d",&x); 100 u=N*K+(i-1)*K+j; 101 addedge(u,t,x,0); 102 } 103 } 104 for ( i=1;i<=K;i++ ) { 105 for ( j=1;j<=N;j++ ) { 106 for ( k=1;k<=M;k++ ) { 107 scanf("%d",&x); 108 u=(j-1)*K+i; 109 v=(k-1)*K+i+N*K; 110 addedge(u,v,inf,x); 111 } 112 } 113 } 114 ans=mincost(); 115 if ( ans<sum ) printf("-1\n"); 116 else printf("%lld\n",cost); 117 } 118 return 0; 119 }
然后考虑到对于每个货物来说都是各自独立的,所有每次建图只是考虑第i件货物,然后将K的结果加起来,就将本来的拆点过程转变成分解状态。同时要注意细节,清除输入的量的顺序是什么。一般这种分解状态适用于在原本只有两部分(商店和仓库),但因为货物的存在使得两边各自都需要拆点,而对于每个货物来说都是各自独立,互不影响的。这时候可以考虑进行多次的构图,然后将最小费用累加。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<queue> 5 #include<vector> 6 using namespace std; 7 const int maxn=5005; 8 const int maxm=55; 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 int n,s,t; 15 long long cost; 16 vector<edge>E; 17 vector<int>G[maxn]; 18 bool vis[maxn]; 19 int d[maxn],p[maxn],a[maxn],nk[maxm][maxm],mk[maxm][maxm],mp[maxm][maxm][maxm],col[maxm],row[maxm]; 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 u,int v,int f,int c) 28 { 29 E.push_back(edge(u,v,f,c)); 30 E.push_back(edge(v,u,0,-c)); 31 int m=E.size(); 32 G[u].push_back(m-2); 33 G[v].push_back(m-1); 34 } 35 36 bool bellmanford(int& flow) 37 { 38 for ( int i=0;i<=n;i++ ) d[i]=inf; 39 memset(vis,false,sizeof(vis)); 40 d[s]=0; 41 vis[s]=true; 42 p[s]=0; 43 a[s]=inf; 44 queue<int>que; 45 que.push(s); 46 while ( !que.empty() ) { 47 int u=que.front(); 48 que.pop(); 49 vis[u]=false; 50 for ( int i=0;i<G[u].size();i++ ) { 51 edge& e=E[G[u][i]]; 52 if ( e.flow>0 && d[e.to]>d[u]+e.cost ) { 53 d[e.to]=d[u]+e.cost; 54 p[e.to]=G[u][i]; 55 a[e.to]=min(a[u],e.flow); 56 if ( !vis[e.to] ) { 57 que.push(e.to); 58 vis[e.to]=true; 59 } 60 } 61 } 62 } 63 if ( d[t]==inf ) return false; 64 flow+=a[t]; 65 cost+=(long long)d[t]*(long long)a[t]; 66 for ( int u=t;u!=s;u=E[p[u]].from ) { 67 E[p[u]].flow-=a[t]; 68 E[p[u]^1].flow+=a[t]; 69 } 70 return true; 71 } 72 73 int mincost() 74 { 75 int flow=0; 76 cost=0; 77 while ( bellmanford(flow)); 78 return flow; 79 } 80 81 int main() 82 { 83 int N,M,K,i,j,k,x,y,z,u,v,ans,now; 84 long long sum; 85 bool flag; 86 while ( scanf("%d%d%d",&N,&M,&K)!=EOF && (N+M+K) ) { 87 memset(col,0,sizeof(col)); 88 memset(row,0,sizeof(row)); 89 for ( i=1;i<=N;i++ ) { 90 for ( j=1;j<=K;j++ ) { 91 scanf("%d",&nk[i][j]); 92 row[j]+=nk[i][j]; 93 } 94 } 95 for ( i=1;i<=M;i++ ) { 96 for ( j=1;j<=K;j++ ) { 97 scanf("%d",&mk[i][j]); 98 col[j]+=mk[i][j]; 99 } 100 } 101 for ( i=1;i<=K;i++ ) { 102 for ( j=1;j<=N;j++ ) { 103 for ( k=1;k<=M;k++ ) scanf("%d",&mp[i][j][k]); 104 } 105 } 106 long long sum=0; 107 flag=true; 108 for ( i=1;i<=K;i++ ) { 109 if ( col[i]<row[i] ) { 110 flag=false; 111 break; 112 } 113 } 114 if ( !flag ) { 115 printf("-1\n"); 116 continue; 117 } 118 s=0; 119 n=N+M+1; 120 t=n; 121 for ( i=1;i<=K;i++ ) { 122 init(); 123 for ( j=1;j<=N;j++ ) { 124 addedge(s,j,nk[j][i],0); 125 } 126 for ( j=1;j<=M;j++ ) { 127 addedge(j+N,t,mk[j][i],0); 128 } 129 for ( j=1;j<=N;j++ ) { 130 for ( k=1;k<=M;k++ ) { 131 addedge(j,k+N,inf,mp[i][j][k]); 132 } 133 } 134 mincost(); 135 sum+=cost; 136 } 137 printf("%lld\n",sum); 138 } 139 return 0; 140 }
7.(POJ3422)http://poj.org/problem?id=3422
题意:给定一个N*N矩阵,每个位置都有一个权值,总共有K次旅途,每次都要从左上方走到右下方,每走一次可以加上该点的权值,某点的权值被加过一次后变成0,求权值的最大和是多少
分析:最小费用最大流,本来考虑将每个点都拆成K个,后来发现数据量太大无法实现,后来想到对于走过一次后的点都是一样等价的(都是费用为0的点),只需要将容量扩大即可。而对于点权的处理考虑拆边,建两条边一条费用为-点权,容量为1,令一条费用为0,容量为inf(>=k-1都可以),K次操作通过超级源点到左上方的点容量为1即可实现。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<queue> 5 #include<vector> 6 using namespace std; 7 const int maxn=5005; 8 const int inf=1e9; 9 const int maxm=55; 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 int n,s,t; 15 long long cost; 16 vector<edge>E; 17 vector<int>G[maxn]; 18 bool vis[maxn]; 19 int d[maxn],p[maxn],a[maxn],mp[maxm][maxm]; 20 int dir[2][2]={{1,0},{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 u,int v,int f,int c) 29 { 30 E.push_back(edge(u,v,f,c)); 31 E.push_back(edge(v,u,0,-c)); 32 int m=E.size(); 33 G[u].push_back(m-2); 34 G[v].push_back(m-1); 35 } 36 37 bool bellmanford(int& flow) 38 { 39 for ( int i=0;i<=n;i++ ) d[i]=inf; 40 memset(vis,false,sizeof(vis)); 41 d[s]=0; 42 vis[s]=true; 43 p[s]=0; 44 a[s]=inf; 45 queue<int>que; 46 que.push(s); 47 while ( !que.empty() ) { 48 int u=que.front(); 49 que.pop(); 50 vis[u]=false; 51 for ( int i=0;i<G[u].size();i++ ) { 52 edge& e=E[G[u][i]]; 53 if ( e.flow>0 && d[e.to]>d[u]+e.cost ) { 54 d[e.to]=d[u]+e.cost; 55 p[e.to]=G[u][i]; 56 a[e.to]=min(a[u],e.flow); 57 if ( !vis[e.to] ) { 58 que.push(e.to); 59 vis[e.to]=true; 60 } 61 } 62 } 63 } 64 if ( d[t]==inf ) return false; 65 flow+=a[t]; 66 cost+=(long long)d[t]*(long long)a[t]; 67 for ( int u=t;u!=s;u=E[p[u]].from ) { 68 E[p[u]].flow-=a[t]; 69 E[p[u]^1].flow+=a[t]; 70 } 71 return true; 72 } 73 74 int mincost() 75 { 76 int flow=0; 77 cost=0; 78 while ( bellmanford(flow)); 79 return flow; 80 } 81 82 int main() 83 { 84 int N,K,i,j,k,x,y,z,u,v; 85 while ( scanf("%d%d",&N,&K)!=EOF ) { 86 s=0; 87 n=N*N*2+1; 88 t=n; 89 init(); 90 for ( i=1;i<=N;i++ ) { 91 for ( j=1;j<=N;j++ ) { 92 scanf("%d",&mp[i][j]); 93 } 94 } 95 for ( i=1;i<=N;i++ ) { 96 for ( j=1;j<=N;j++ ) { 97 u=(i-1)*N+j; 98 addedge(u,u+N*N,1,-mp[i][j]); 99 addedge(u,u+N*N,inf,0); 100 for ( k=0;k<2;k++ ) { 101 x=i+dir[k][0]; 102 y=j+dir[k][1]; 103 v=(x-1)*N+y; 104 if ( x<=N && y<=N ) { 105 addedge(u+N*N,v,inf,0); 106 } 107 } 108 } 109 } 110 addedge(s,1,K,0); 111 addedge(t-1,t,K,0); 112 mincost(); 113 printf("%lld\n",-cost); 114 } 115 return 0; 116 }