HDU 1569 方格取数(2)(最大流最小割の最大权独立集)

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\n", 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\n", sum - G.Max_flow(ss, tt, tt));
115     }
116 }
View Code

 

posted @ 2013-08-10 18:07  Oyking  阅读(324)  评论(0编辑  收藏  举报