HIT 2715 - Matrix3 - [最小费用最大流][数组模拟邻接表MCMF模板]
题目链接:http://acm.hit.edu.cn/hoj/problem/view?id=2715
Time limit : 5 sec Memory limit : 64 M
Zhouguyue is a "驴友" and nowadays he likes traveling on an N * N matrix with a non-negative number in each grid, and each grid has a height. Zhouguyue starts his matrix travel with POINT = 0. For each travel, zhouguyue can land on any grid he wants with the help of bin3's helicopter, and then he can only move to ajacent grids whose height is less than his current height. Notice that when he is at the side of the matrix, he can also move out of the matrix. After he moves out of the matrix, he completes one travel. He adds the number in each grid he visited to POINT, and replaces it with zero. Now zhouguyue is wondering what is the maximum POINT he can obtain after he travels at most K times. Note the POINT is accumulative during the travels.
Input
The first line is a integer T indicating the number of test cases.T cases fllows. The first line of each case contains two integers N and K (1 ≤ N ≤ 50, 0 ≤ K ≤ 50) described above. The following N lines represents the matrix. You can assume the numbers in the matrix are non-negative integers and no more than 10000. The following N lines represents the height of each grid. The heghts are also non-negative integers.
Output
The maximum POINT zhouguyue can obtain after he travels at most K times.
Sample Input
1 3 2 1 2 3 3 2 1 2 4 2 3 5 3 2 1 0 1 2 3
Sample Output
17
题意:
一个 N*N 的网格,每个单元都有一个数字number[i][j],和一个高度height[i][j]。
现在 ZhouGuyue 要作至多 K 次旅行,每次旅行如下:
他可以借助 bin3 的直升机飞到 任意一个单元格,之后他每次只能向相邻的且高度比当前所在格子低的格子移动。
当他移动到一个边界的格子上时,他可以跳出这个网格并完成一次旅行。
旅行开始前,他有一个POINT,初始值为0;旅行途中,所到的每一格的number[i][j]可以加到他的POINT里,一旦加上,格子里number[i][j]就变成0。
问POINT最大有多大。(1 <= N <= 50, 0 <= K <= 50, 0 <= Vi <= 10000)
解题思路:
本题属于最大费用最大流问题,对cost取负后转变成为最小费用最大流问题,构图完成后可以直接上模板,最后求出mincost再取个负即为答案。
构图:
将每个格子 i 进行拆点,并加边(i’, i’’, 1, -number[i]), (i’, i’’, ∞, 0), (s, i’, ∞, 0);(分别对应from,to,cap,cost)
这代表,他走第一次,可以走(i’, i’’, 1, -number[i]),由于算法保证最小费用,他也只会先走(i’, i’’, 1, -number[i]);
走过这个格子,可以得到number,然后后面虽然可以继续走这个格子,但只能通过(i’, i’’, ∞, 0)这条边走,就得不到number;
另外,由于他可以从任意格子出发,故要建立超级源点s,连接每个格子。
对相邻的四个格子 j,若 Hi > Hj 则加边(i’’, j’, ∞, 0);
这条边很简单,代表可以同行,并且想走多少次都可以。
若格子 i在边界上则加边(i’’, t, ∞, 0);
代表走到边界,可以直接结束本次旅行,由于边界上有许多格子,故也要建立超级汇点。
限制增广次数小于等于 K 。
有增广一次可以看做沿某个路线走了一遍,并且获得了沿途的每个格子的number,增广次数不超过K,即代表旅行次数最多为K。
注意要点:
HIT的OJ可能卡STL,使用vector做邻接表超时(然后就只好自己写数组模拟邻接表呗,不过改一下也挺快,没啥问题);
如果使用的是刘汝佳书上的MCMF模板,while(spfa())语句增加限制次数K要注意写法,我就是没考虑到K==0的情况,刚开始写挫了,WA了一发;
代码:
1 #include<cstdio> 2 #include<cstring> 3 #include<vector> 4 #include<queue> 5 #define MAXN 2*53*53 6 #define MAXM 8*MAXN 7 #define INF 0x3f3f3f3f 8 #define in(x) x 9 #define out(x) x+n 10 using namespace std; 11 int n,N,K; 12 struct Edge{ 13 int u,v,c,f,a; 14 }; 15 struct MCMF 16 { 17 int s,t,ne; 18 Edge E[MAXM]; 19 int head[MAXN],next[MAXM]; 20 int vis[MAXN]; 21 int d[MAXN]; 22 int pre[MAXN]; 23 int aug[MAXN]; 24 void init() 25 { 26 ne=0; 27 memset(head,-1,sizeof(head)); 28 } 29 void addedge(int from,int to,int cap,int cost) 30 { 31 E[ne].u=from, E[ne].v=to, E[ne].c=cap, E[ne].f=0, E[ne].a=cost; 32 next[ne]=head[from]; head[from]=ne++; 33 E[ne].u=to, E[ne].v=from, E[ne].c=0, E[ne].f=0, E[ne].a=-cost; 34 next[ne]=head[to]; head[to]=ne++; 35 } 36 bool SPFA(int s,int t,int &flow,int &cost) 37 { 38 memset(d,INF,sizeof(d)); 39 memset(vis,0,sizeof(vis)); 40 d[s]=0, vis[s]=1, pre[s]=0, aug[s]=INF; 41 queue<int> q; 42 q.push(s); 43 while(!q.empty()) 44 { 45 int now=q.front(); q.pop(); 46 vis[now]=0; 47 for(int i=head[now];i!=-1;i=next[i]) 48 { 49 Edge& e=E[i]; 50 int nex=e.v; 51 if(e.c>e.f && d[nex]>d[now]+e.a) 52 { 53 d[nex]=d[now]+e.a; 54 pre[nex]=i; 55 aug[nex]=min(aug[now],e.c-e.f); 56 if(!vis[nex]) 57 { 58 q.push(nex); 59 vis[nex]=1; 60 } 61 } 62 } 63 } 64 if(d[t]==INF) return 0; 65 flow+=aug[t]; 66 cost+=d[t]*aug[t]; 67 for(int i=t;i!=s;i=E[pre[i]].u) 68 { 69 E[pre[i]].f+=aug[t]; 70 E[pre[i]^1].f-=aug[t]; 71 } 72 return 1; 73 } 74 int mincost() 75 { 76 int flow=0,cost=0,cnt=0; 77 while(cnt<K && SPFA(s,t,flow,cost)) cnt++; 78 return cost; 79 } 80 }mcmf; 81 82 int number[53][53],height[53][53]; 83 int d[4][2]={{0,1},{1,0},{0,-1},{-1,0}}; 84 int inmap(int i,int j) 85 { 86 if(1<=i && i<=N && 1<=j && j<=N) return (i-1)*N+j; 87 else return 0; 88 } 89 int main() 90 { 91 int t; 92 scanf("%d",&t); 93 while(t--) 94 { 95 scanf("%d%d",&N,&K); 96 for(int i=1;i<=N;i++) for(int j=1;j<=N;j++) scanf("%d",&number[i][j]); 97 for(int i=1;i<=N;i++) for(int j=1;j<=N;j++) scanf("%d",&height[i][j]); 98 n=N*N; 99 mcmf.init(); 100 mcmf.s=0, mcmf.t=2*n+1; 101 for(int i=1;i<=N;i++) 102 { 103 for(int j=1;j<=N;j++) 104 { 105 int id=(i-1)*N+j; 106 mcmf.addedge(mcmf.s,in(id),INF,0); 107 mcmf.addedge(in(id),out(id),1,-number[i][j]); 108 mcmf.addedge(in(id),out(id),INF,0); 109 if(i==1 || i==N || j==1 || j==N) mcmf.addedge(out(id),mcmf.t,INF,0); 110 for(int k=0;k<4;k++) 111 { 112 int nex_i=i+d[k][0], nex_j=j+d[k][1],nex_id; 113 if((nex_id=inmap(nex_i,nex_j)) && height[i][j]>height[nex_i][nex_j]) mcmf.addedge(out(id),in(nex_id),INF,0); 114 } 115 } 116 } 117 printf("%d\n",-mcmf.mincost()); 118 } 119 }