Description
给你一个m*n的格子的棋盘,每个格子里面有一个非负数。 从中取出若干个数,使得任意的两个数所在的格子没有公共边,就是说所取数所在的2个格子不能相邻,并且取出的数的和最大。
Input
包括多个测试实例,每个测试实例包括2整数m,n和m*n个非负数(m<=50,n<=50)
Output
对于每个测试实例,输出可能取得的最大的和
题目大意:这么短还中文就没大意了,唯一要注意的就是输入的第一个是行第二个是列……
思路:这题为最大权独立集(所选的点之间都没有边)。建立一个最大流的二分图,i+j为偶数的放左边,源点S连一条边到它,容量为格子里的数,其余放右边,连一条边到汇点T,容量还是格子里的数,相邻的都从左到右连一条边,容量为无穷大。所有数字之和减去最大流即为答案。
小证明:这样构图求出的最大流为最小权覆盖集(所有边至少被一个点覆盖),详见POJ 3308 Paratroopers(最大流最小割の最小点权覆盖)
而最小权覆盖集与最大权独立集是对偶图,把最小权覆盖集里的点都取反,就可以得到一个最大权独立集,所以总权 = 最小权覆盖集 + 最大权独立集。详见二分图中的对偶问题
代码(15MS):
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #include <queue> 5 using namespace std; 6 7 const int MAXN = 3000; 8 const int MAXE = 30010; 9 const int INF = 0x3fff3fff; 10 11 struct SAP { 12 int head[MAXN], dis[MAXN], pre[MAXN], cur[MAXN], gap[MAXN]; 13 int to[MAXE], next[MAXE], flow[MAXE]; 14 int n, st, ed, ecnt; 15 16 void init() { 17 memset(head, 0, sizeof(head)); 18 ecnt = 2; 19 } 20 21 void add_edge(int u, int v, int c) { 22 to[ecnt] = v; flow[ecnt] = c; next[ecnt] = head[u]; head[u] = ecnt++; 23 to[ecnt] = u; flow[ecnt] = 0; next[ecnt] = head[v]; head[v] = ecnt++; 24 //printf("%d->%d flow = %d ", u, v, c); 25 } 26 27 void bfs() { 28 memset(dis, 0x3f, sizeof(dis)); 29 queue<int> que; que.push(ed); 30 dis[ed] = 0; 31 while(!que.empty()) { 32 int u = que.front(); que.pop(); 33 ++gap[dis[u]]; 34 for(int p = head[u]; p; p = next[p]) { 35 int &v = to[p]; 36 if(flow[p ^ 1] && dis[v] > n) { 37 dis[v] = dis[u] + 1; 38 que.push(v); 39 } 40 } 41 } 42 } 43 44 int Max_flow(int ss, int tt, int nn) { 45 st = ss; ed = tt; n = nn; 46 int ans = 0, minFlow = INF, u; 47 for(int i = 0; i <= n; ++i) { 48 cur[i] = head[i]; 49 gap[i] = 0; 50 } 51 u = pre[st] = st; 52 bfs(); 53 while(dis[st] < n) { 54 bool flag = false; 55 for(int &p = cur[u]; p; p = next[p]) { 56 int &v = to[p]; 57 if(flow[p] && dis[u] == dis[v] + 1) { 58 flag = true; 59 minFlow = min(minFlow, flow[p]); 60 pre[v] = u; 61 u = v; 62 if(u == ed) { 63 ans += minFlow; 64 while(u != st) { 65 u = pre[u]; 66 flow[cur[u]] -= minFlow; 67 flow[cur[u] ^ 1] += minFlow; 68 } 69 minFlow = INF; 70 } 71 break; 72 } 73 } 74 if(flag) continue; 75 int minDis = n - 1; 76 for(int p = head[u]; p; p = next[p]) { 77 int &v = to[p]; 78 if(flow[p] && minDis > dis[v]) { 79 minDis = dis[v]; 80 cur[u] = p; 81 } 82 } 83 if(--gap[dis[u]] == 0) break; 84 gap[dis[u] = minDis + 1]++; 85 u = pre[u]; 86 } 87 return ans; 88 } 89 } G; 90 91 int n, m; 92 int mat[55][55]; 93 94 int main() { 95 while(scanf("%d%d", &n, &m) != EOF) { 96 for(int i = 1; i <= n; ++i) 97 for(int j = 1; j <= m; ++j) scanf("%d", &mat[i][j]); 98 G.init(); 99 int ss = n * m + 1, tt = n * m + 2; 100 int cnt = 0, sum = 0; 101 for(int i = 1; i <= n; ++i) { 102 for(int j = 1; j <= m; ++j) { 103 ++cnt; sum += mat[i][j]; 104 if((i + j) & 1) { 105 G.add_edge(ss, cnt, mat[i][j]); 106 if(j != 1) G.add_edge(cnt, cnt - 1, INF); 107 if(i != 1) G.add_edge(cnt, cnt - m, INF); 108 if(j != m) G.add_edge(cnt, cnt + 1, INF); 109 if(i != n) G.add_edge(cnt, cnt + m, INF); 110 } 111 else G.add_edge(cnt, tt, mat[i][j]); 112 } 113 } 114 printf("%d ", sum - G.Max_flow(ss, tt, tt)); 115 } 116 }