限制增广次数的费用流。
【题目大意】
一个N*N的网格,每个单元都有一个价值Vi的宝物和一个高度Hi。现在ZhouGuyue要作至多K次旅行,每次旅行如下:他可以借助bin3的直升机飞到任意一个单元,之后他每次只能向相邻的且高度比当前所在格子低的格子移动。当他移动到一个边界的格子上时,他可以跳出这个网格并完成一次旅行。旅行中所到之处的宝物他可以全部拿走,一旦拿走原来的格子里就没有宝物了。问他最多能拿走价值多少的宝物。(1 <= N <= 50, 0 <= K <= 50, 0 <= Vi <= 10000)
【建模方法】
将每个格子i拆成两个点i’, i’’并加边(i’, i’’, 1, -Vi), (i’, i’’, ∞, 0), (s, i’, ∞, 0);对相邻的四个格子j,若Hi > Hj则加边(i’’, j’, ∞, 0);若格子i在边界上则加边(i’’, t, ∞, 0)。限制增广次数小于等于K求最小费用流即可。
以上摘自Edelweiss《网络流建模汇总》
启示:
1.增广次数做了限制的问题还是比较好解决的,对每次增广后用计数器次数统计就可以了。
2.这里要求费用尽量大,可以把所有费用赋为-value,然后做费用流,得到结果的相反数就是最大的费用。
3.有的边,当第一次经过它时,可获利cost,以后经过便不再获利,则加两天边(u,v,1,-cost),(u,v,INF,0)。其实就是每种情况都对应一条边。同时,由最短路的性质,当前节点u一定会选择一条边权尽量小的边线走,因此可以保证第一次经过时的获利。
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <cmath> 5 #include <queue> 6 #define maxn 5010 7 #define maxm 100000 8 #define INF 1<<30 9 using namespace std; 10 11 int N,K; 12 struct MCMF{ 13 int src,sink,e,n; 14 int first[maxn]; 15 int cap[maxm],cost[maxm],v[maxm],next[maxm]; 16 bool flag; 17 void init(){ 18 e = 0; 19 memset(first,-1,sizeof(first)); 20 } 21 22 void add_edge(int a,int b,int cc,int ww){ 23 //printf("add:%d to %d,cap = %d,cost = %d ",a,b,cc,ww); 24 cap[e] = cc;cost[e] = ww;v[e] = b; 25 next[e] = first[a];first[a] = e++; 26 cap[e] = 0;cost[e] = -ww;v[e] = a; 27 next[e] = first[b];first[b] = e++; 28 } 29 30 int d[maxn],pre[maxn],pos[maxn]; 31 bool vis[maxn]; 32 33 bool spfa(int s,int t){ 34 memset(pre,-1,sizeof(pre)); 35 memset(vis,0,sizeof(vis)); 36 queue<int> Q; 37 for(int i = 0;i <= n;i++) d[i] = INF; 38 Q.push(s);pre[s] = s;d[s] = 0;vis[s] = 1; 39 while(!Q.empty()){ 40 int u = Q.front();Q.pop(); 41 vis[u] = 0; 42 for(int i = first[u];i != -1;i = next[i]){ 43 if(cap[i] > 0 && d[u] + cost[i] < d[v[i]]){ 44 d[v[i]] = d[u] + cost[i]; 45 pre[v[i]] = u;pos[v[i]] = i; 46 if(!vis[v[i]]) vis[v[i]] = 1,Q.push(v[i]); 47 } 48 } 49 } 50 return pre[t] != -1; 51 } 52 53 int Mincost; 54 int Maxflow; 55 56 int MinCostFlow(int s,int t,int nn){ 57 Mincost = 0,Maxflow = 0,n = nn; 58 int cnt = 0; 59 while(spfa(s,t)){ 60 cnt++; 61 if(cnt > K) break; 62 int min_f = INF; 63 for(int i = t;i != s;i = pre[i]) 64 if(cap[pos[i]] < min_f) min_f = cap[pos[i]]; 65 Mincost += d[t] * min_f; 66 Maxflow += min_f; 67 for(int i = t;i != s;i = pre[i]){ 68 cap[pos[i]] -= min_f; 69 cap[pos[i]^1] += min_f; 70 } 71 } 72 return Mincost; 73 } 74 }; 75 76 MCMF g; 77 int value[55][55],h[55][55]; 78 79 inline int id(int i,int j){ 80 return i*N + j; 81 } 82 83 int dx[] = {0,1,0,-1}; 84 int dy[] = {1,0,-1,0}; 85 int main(){ 86 int kase; 87 scanf("%d",&kase); 88 while(kase--){ 89 g.init(); 90 scanf("%d%d",&N,&K); 91 for(int i = 0;i < N;i++) 92 for(int j = 0;j < N;j++) 93 scanf("%d",&value[i][j]); 94 for(int i = 0;i < N;i++) 95 for(int j = 0;j < N;j++) 96 scanf("%d",&h[i][j]); 97 int S = N*N*2,T = S+1; 98 for(int i = 0;i < N;i++){ 99 for(int j = 0;j < N;j++){ 100 g.add_edge(S,id(i,j),INF,0); 101 g.add_edge(id(i,j),id(i,j)+N*N,1,-value[i][j]); 102 g.add_edge(id(i,j),id(i,j)+N*N,INF,0); 103 for(int k = 0;k < 4;k++){ 104 int x = i + dx[k]; 105 int y = j + dy[k]; 106 if(x < 0 || x >= N || y < 0 || y >= N) continue; 107 if(h[i][j] <= h[x][y]) continue; 108 g.add_edge(id(i,j)+N*N,id(x,y),INF,0); 109 } 110 if(i == 0 || i == N-1 || j == 0 || j == N-1){ 111 g.add_edge(id(i,j)+N*N,T,INF,0); 112 } 113 } 114 } 115 //printf("OK "); 116 int ans = g.MinCostFlow(S,T,T); 117 printf("%d ",-ans); 118 } 119 }