http://acm.hdu.edu.cn/showproblem.php?pid=1565
算法:
最大点权独立集 = 总点数 - 最小点权覆盖集
最小点权覆盖集 = 最小割 = 最大流
点独立集:点独立集是无向图G的子集,该子集的导出子图不含边,即任两个在该集合中的点在原图中都不相邻。
最大点权独立集:在带权无向图G中,点权之和最大的独立集。
点覆盖集:点覆盖集是无向图G的子集,使得原图内所有边都至少有一个端点在该集合内。
最小点权覆盖集:在带权无向图G中,点权之和最小的覆盖集。
思路:
要求最大点权独立集,根据算法只需求最小点权覆盖集,也就是求最大流问题。
先染色,纵横坐标之和满足 (i+j)%2 == 0 的点染白色,与它相邻的点染黑色;
源点s与每个白点相连,容量为该点的数字;
每个白点与它相邻的黑点相连,容量为无穷大;
每个黑点与汇点相连,容量为该点上的数字;
对这个图求最大流maxflow,用所有数字和减maxflow就是答案。
#include<stdio.h> #include<string.h> #include<queue> #include<algorithm> using namespace std; const int INF = 0x3f3f3f3f; int n,sum,s,t; int map[30][30]; int cap[500][500];//容量 int flow[500][500];//流量 int p[500];//记录前驱 int a[500];//记录该路径上的最小增量 int maxflow; queue<int> que; void E_K()//bfs找增光路 { while(!que.empty()) que.pop(); memset(flow,0,sizeof(flow)); while(1) { for(int i = 0; i <= t; i++) p[i] = i; memset(a,0,sizeof(a)); a[s] = INF; que.push(s); while(!que.empty()) { int u = que.front(); que.pop(); for(int v = 0; v <= t; v++) { if(!a[v] && flow[u][v] < cap[u][v]) { p[v] = u; que.push(v); a[v] = min(a[u],cap[u][v]-flow[u][v]); } } } if(a[t] == 0) break; for(int u = t; u != s; u = p[u]) { flow[p[u]][u] += a[t]; flow[u][p[u]] -= a[t]; } maxflow += a[t]; } } int main() { while(~scanf("%d",&n)) { sum = 0; for(int i = 1; i <= n; i++) { for(int j = 1; j <= n; j++) { scanf("%d",&map[i][j]); sum += map[i][j]; } } memset(cap,0,sizeof(cap)); s = 0; t = n*n+1; for(int i = 1; i <= n; i++) { for(int j = 1; j <= n; j++) { int it = (i-1)*n+j; if((i+j)%2 == 0) cap[s][it] = map[i][j]; else cap[it][t] = map[i][j]; } } for(int i = 1; i <= n; i++) { for(int j = 1; j <= n; j++) { if((i+j)%2 == 0) { if(j < n) cap[(i-1)*n+j][(i-1)*n+j+1] = INF; if(j > 1) cap[(i-1)*n+j][(i-1)*n+j-1] = INF; if(i > 1) cap[(i-1)*n+j][(i-1)*n+j-n] = INF; if(i < n) cap[(i-1)*n+j][(i-1)*n+j+n] = INF; } } } maxflow = 0; E_K(); printf("%d\n",sum-maxflow); } return 0; }