HDU1569 方格取数(2) —— 二分图点带权最大独立集、最小割最大流
题目链接:https://vjudge.net/problem/HDU-1569
方格取数(2)
Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 6876 Accepted Submission(s): 2198
Problem Description
给你一个m*n的格子的棋盘,每个格子里面有一个非负数。
从中取出若干个数,使得任意的两个数所在的格子没有公共边,就是说所取数所在的2个格子不能相邻,并且取出的数的和最大。
从中取出若干个数,使得任意的两个数所在的格子没有公共边,就是说所取数所在的2个格子不能相邻,并且取出的数的和最大。
Input
包括多个测试实例,每个测试实例包括2整数m,n和m*n个非负数(m<=50,n<=50)
Output
对于每个测试实例,输出可能取得的最大的和
Sample Input
3 3
75 15 21
75 15 28
34 70 5
Sample Output
188
Author
ailyanlu
Source
题解:
1.将n*m的格子建模成二分图。
2.在二分图的基础上添加源点S和汇点T, 然后从S向所有X集合中的点连一条边, 所有Y集合中的点向T连一条边, 容量均为该点的权值。
3.X集合的结点与Y集合的结点之间的边的容量为无穷大。在此题中,如果是格子相邻,就需要连边,且是X-->Y。
4.因此,对于该图中的任意一个割, 将割中的边所对应的结点删掉,就是一个符合条件的解,权值和为所有权和减去割的容量。
5.根据4可得:只要求出最小割, 就能求出最大权值和独立集。
代码如下:
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 #include <vector> 6 #include <cmath> 7 #include <queue> 8 #include <stack> 9 #include <map> 10 #include <string> 11 #include <set> 12 using namespace std; 13 typedef long long LL; 14 const int INF = 2e9; 15 const LL LNF = 9e18; 16 const int mod = 1e9+7; 17 const int MAXM = 1e5+10; 18 const int MAXN = 2500+10; 19 20 struct Edge 21 { 22 int to, next, cap, flow; 23 }edge[MAXM]; 24 int tot, head[MAXN]; 25 int gap[MAXN], dep[MAXN], pre[MAXN], cur[MAXN]; 26 27 void init() 28 { 29 tot = 0; 30 memset(head, -1, sizeof(head)); 31 } 32 33 void add(int u, int v, int w) 34 { 35 edge[tot].to = v; edge[tot].cap = w; edge[tot].flow = 0; 36 edge[tot].next = head[u]; head[u] = tot++; 37 edge[tot].to = u; edge[tot].cap = 0; edge[tot].flow = 0; 38 edge[tot].next = head[v]; head[v] = tot++; 39 } 40 41 int sap(int start, int end, int nodenum) 42 { 43 memset(dep, 0, sizeof(dep)); 44 memset(gap, 0, sizeof(gap)); 45 memcpy(cur, head, sizeof(head)); 46 int u = pre[start] = start, maxflow = 0,aug = INF; 47 gap[0] = nodenum; 48 while(dep[start]<nodenum) 49 { 50 loop: 51 for(int i = cur[u]; i!=-1; i = edge[i].next) 52 { 53 int v = edge[i].to; 54 if(edge[i].cap-edge[i].flow && dep[u]==dep[v]+1) 55 { 56 aug = min(aug, edge[i].cap-edge[i].flow); 57 pre[v] = u; 58 cur[u] = i; 59 u = v; 60 if(v==end) 61 { 62 maxflow += aug; 63 for(u = pre[u]; v!=start; v = u,u = pre[u]) 64 { 65 edge[cur[u]].flow += aug; 66 edge[cur[u]^1].flow -= aug; 67 } 68 aug = INF; 69 } 70 goto loop; 71 } 72 } 73 int mindis = nodenum; 74 for(int i = head[u]; i!=-1; i = edge[i].next) 75 { 76 int v=edge[i].to; 77 if(edge[i].cap-edge[i].flow && mindis>dep[v]) 78 { 79 cur[u] = i; 80 mindis = dep[v]; 81 } 82 } 83 if((--gap[dep[u]])==0)break; 84 gap[dep[u]=mindis+1]++; 85 u = pre[u]; 86 } 87 return maxflow; 88 } 89 90 int n, m, g[55][55]; 91 int main() 92 { 93 while(scanf("%d%d", &n, &m)!=EOF) 94 { 95 int sum = 0; 96 for(int i = 0; i<n; i++) 97 for(int j = 0; j<m; j++) 98 scanf("%d", &g[i][j]), sum += g[i][j]; 99 100 int start = n*m, end = n*m+1; 101 init(); 102 for(int i = 0; i<n; i++) 103 for(int j = 0; j<m; j++) 104 { 105 if((i+j)%2) 106 { 107 add(start, i*m+j, g[i][j]); 108 if(i!=0) add(i*m+j, (i-1)*m+j, INF); 109 if(i!=n-1) add(i*m+j, (i+1)*m+j, INF); 110 if(j!=0) add(i*m+j, i*m+j-1, INF); 111 if(j!=m-1) add(i*m+j, i*m+j+1, INF); 112 } 113 else 114 add(i*m+j, end, g[i][j]); 115 } 116 117 sum -= sap(start, end, n*m+2); 118 printf("%d\n", sum); 119 } 120 }