题意:给你一个m*n的格子的棋盘,每个格子里面有一个非负数。从中取出若干个数,使得任意的两个数所在的格子没有公共边,就是说所取数所在的2个格子不能相邻,并且取出的数的和最大。
链接:点我
分析转自:点我
二分图最小点覆盖和最大独立集都可以转化为最大匹配求解。在这个基础上,把每个点赋予一个非负的权值,这两个问题就转化为:二分图最小点权覆盖和二分图最大点权独立集。
二分图最小点权覆盖
从x或者y集合中选取一些点,使这些点覆盖所有的边,并且选出来的点的权值尽可能小。
建模:
原二分图中的边(u,v)替换为容量为INF的有向边(u,v),设立源点s和汇点t,将s和x集合中的点相连,容量为该点的权值;将y中的点同t相连,容量为该点的权值。在新图上求最大流,最大流量即为最小点权覆盖的权值和。
二分图最大点权独立集
在二分图中找到权值和最大的点集,使得它们之间两两没有边。其实它是最小点权覆盖的对偶问题。答案=总权值-最小点覆盖集。具体证明参考胡波涛的论文。
先理理概念:
点覆盖集:无向图G的一个点集,使得该图中所有边都至少有一个端点在该集合内。
最小点权覆盖集:在带点权无向图G中,点权之和最小的覆盖集。
点独立集:无向图G的一个点集,使得任两个在该集合中的点在原图中都不相邻。
最大点权独立集:在带权无向图G中,点权之和最大的独立集。
定理:
1. 最小点权覆盖集=最小割=最大流
2. 最大点权独立集=总权-最小点权覆盖集
思路:
1. 先染色,取一个点染白色,和它相邻的点染黑色
2. 每个白点向它相邻的黑点连一条边,容量为 inf (无穷大)
3. 增加源点S,向每一个白色点连一条边,容量为白点的权
4. 增加汇点T,每个黑点向T连一条边,容量为黑点的权
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 using namespace std; 5 #define MAXN 5555 6 #define MAXM 222222 7 #define inf 1<<30 8 9 struct Edge{ 10 int v,cap,next; 11 }edge[MAXM]; 12 13 int head[MAXN]; 14 int pre[MAXN]; 15 int cur[MAXN]; 16 int level[MAXN]; 17 int gap[MAXN]; 18 int NE,NV,n,vs,vt; 19 int dir[4][2]={{0,-1},{0,1},{-1,0},{1,0}}; 20 int map[55][55]; 21 bool mark[55][55]; 22 23 void Insert(int u,int v,int cap,int cc=0){ 24 edge[NE].v=v;edge[NE].cap=cap; 25 edge[NE].next=head[u];head[u]=NE++; 26 27 edge[NE].v=u;edge[NE].cap=cc; 28 edge[NE].next=head[v];head[v]=NE++; 29 } 30 31 int SAP(int vs,int vt){ 32 memset(pre,-1,sizeof(pre)); 33 memset(level,0,sizeof(level)); 34 memset(gap,0,sizeof(gap)); 35 for(int i=0;i<NV;i++)cur[i]=head[i]; 36 int u=pre[vs]=vs,maxflow=0,aug=-1; 37 gap[0]=NV; 38 while(level[vs]<NV){ 39 loop: 40 for(int &i=cur[u];i!=-1;i=edge[i].next){ 41 int v=edge[i].v; 42 if(edge[i].cap&&level[u]==level[v]+1){ 43 aug==-1?aug=edge[i].cap:aug=min(aug,edge[i].cap); 44 pre[v]=u; 45 u=v; 46 if(v==vt){ 47 maxflow+=aug; 48 for(u=pre[u];v!=vs;v=u,u=pre[u]){ 49 edge[cur[u]].cap-=aug; 50 edge[cur[u]^1].cap+=aug; 51 } 52 aug=-1; 53 } 54 goto loop; 55 } 56 } 57 int minlevel=NV; 58 for(int i=head[u];i!=-1;i=edge[i].next){ 59 int v=edge[i].v; 60 if(edge[i].cap&&minlevel>level[v]){ 61 cur[u]=i; 62 minlevel=level[v]; 63 } 64 } 65 gap[level[u]]--; 66 if(gap[level[u]]==0)break; 67 level[u]=minlevel+1; 68 gap[level[u]]++; 69 u=pre[u]; 70 } 71 return maxflow; 72 } 73 74 int main(){ 75 while(~scanf("%d",&n)){ 76 vs=0,vt=n*n+1,NE=0,NV=n*n+2; 77 int sum=0; 78 memset(head,-1,sizeof(head)); 79 memset(mark,false,sizeof(mark)); 80 for(int i=1;i<=n;i++){ 81 for(int j=1;j<=n;j++){ 82 scanf("%d",&map[i][j]); 83 sum+=map[i][j]; 84 } 85 } 86 for(int i=1;i<=n;i++){ 87 for(int j=1;j<=n;j++){ 88 if(!mark[i][j]){ 89 Insert(vs,(i-1)*n+j,map[i][j]); 90 for(int k=0;k<4;k++){ 91 int x=i+dir[k][0]; 92 int y=j+dir[k][1]; 93 if(x>=1&&x<=n&&y>=1&&y<=n){ 94 Insert((i-1)*n+j,(x-1)*n+y,inf); 95 //建图的时候要小心,每个点只能连一次 96 if(!mark[x][y])Insert((x-1)*n+y,vt,map[x][y]); 97 mark[x][y]=true; 98 } 99 } 100 } 101 } 102 } 103 printf("%d ",sum-SAP(vs,vt)); 104 } 105 return 0; 106 }