题意:给你一个n*n的格子的棋盘,每个格子里面有一个非负数。
从中取出若干个数,使得任意的两个数所在的格子没有公共边,就是说所取的数所在的2个格子不能相邻,并且取出的数的和最大。
分析:
定理:最大点权独立集 = 总权值 - 最小割 = 总权值 - 最大流。可以把所有方格分成两个集合,再构造一个源点和一个汇点,源点向其中一个集合中的所有元素都有一条有向边,权值为元素的权值,另一个集合中的所有元素都分别有一条有向边指向汇点,权值为该元素的权值,两个集合之间也构造有向边,由源点集合的点指向汇点集合的点,权值为无穷大。
1 #include <cstdio> 2 #include <cstring> 3 #define M 405 4 struct Node 5 { 6 int c,pos,next;//流量 边终点 7 }E[4000]; 8 9 int pre[M],head[M]; 10 int q[M],front,rear; 11 int path[M],matrix[25][25],dir[4][2] = {-1,0,0,-1,1,0,0,1}; 12 int NE,NV;//边的数目, 点的数目 13 int EK(int s,int t) { 14 int maxflow = 0; 15 while(true) { 16 memset(pre,-1,sizeof(pre)); 17 q[front = rear = 0] = s; 18 while(front <= rear) 19 {//BFS 20 int u = q[front++]; 21 for(int i = head[u]; i != - 1; i = E[i].next ) { 22 int v = E[i].pos; 23 if(pre[v] == -1 && E[i].c > 0){ 24 pre[v] = u; 25 q[++rear] = v; 26 path[v] = i; 27 } 28 } 29 if(pre[t] != -1) 30 break;//达到汇点,跳出BFS 31 } 32 if(pre[t] == -1) 33 break;//无增广路,跳出大循环 34 int aug = -1; 35 for(int v = t ; v != s ; v = pre[v]) { 36 if(aug==-1 || E[path[v]].c<aug){ 37 aug=E[path[v]].c; 38 //寻找最小的delta 39 } 40 } 41 for(int v = t ; v != s ; v = pre[v]) { 42 E[ path[v] ].c -= aug; 43 //流量减去delta 44 E[ path[v]^1 ].c += aug; 45 //????????? 46 } 47 maxflow += aug; 48 } 49 return maxflow; 50 } 51 void Insert(int u,int v,int c,int cc = 0) 52 { 53 E[NE].c = c; E[NE].pos = v; 54 E[NE].next = head[u]; head[u] = NE++; 55 E[NE].c = cc; E[NE].pos = u; 56 E[NE].next = head[v]; head[v] = NE++; 57 } 58 bool inborder(int r,int c,int n) 59 { 60 return r >= 0 && r < n && c >= 0 && c < n; 61 } 62 //源点为n*n,汇点为n*n + 1 63 int main() 64 { 65 int n,sum; 66 while(~scanf("%d",&n)) 67 { 68 NV = n * n + 2; 69 for(int i = 0;i < NV;i++) 70 { 71 head[i] = -1; 72 } 73 sum = 0; 74 for(int i = 0;i < n;i++) 75 { 76 for(int j = 0;j < n;j++) 77 { 78 scanf("%d",&matrix[i][j]); 79 sum += matrix[i][j]; 80 } 81 } 82 NE = 0; 83 for(int i = 0;i < n;i++) 84 { 85 for(int j = 0;j < n;j++) 86 { 87 if((i + j) % 2 == 0) 88 { 89 for(int k = 0;k < 4;k++) 90 { 91 if(inborder(i + dir[k][0],j + dir[k][1],n)) 92 { 93 Insert(i * n + j,(i + dir[k][0]) * n + j + dir[k][1],0xfffffff); 94 } 95 } 96 Insert(n * n,i * n + j,matrix[i][j]); 97 } 98 else 99 { 100 Insert(i * n + j,n * n + 1,matrix[i][j]); 101 } 102 } 103 } 104 printf("%d ",sum - EK(n * n,n * n + 1)); 105 } 106 return 0; 107 }