hdu 1565 方格取数(1) (最小割/状态压缩+DP)

Problem Description
给你一个n*n的格子的棋盘,每个格子里面有一个非负数。
从中取出若干个数,使得任意的两个数所在的格子没有公共边,就是说所取的数所在的2个格子不能相邻,并且取出的数的和最大。
 

Input
包括多个测试实例,每个测试实例包括一个整数n 和n*n个非负数(n<=20)
 

Output
对于每个测试实例,输出可能取得的最大的和
 

Sample Input
   
   
   
   
3 75 15 21 75 15 28 34 70 5
 

Sample Output
188

 

两种做法 : 

点覆盖集:无向图G的一个点集,使得该图中所有边都至少有一个端点在该集合内。
// 最小点权覆盖集:在带点权无向图G中,点权之和最小的覆盖集。
// 点独立集:无向图G的一个点集,使得任两个在该集合中的点在原图中都不相邻。
// 最大点权独立集:在带权无向图G中,点权之和最大的独立集。
// 定理:
// 1. 最小点权覆盖集=最小割=最大流
// 2. 最大点权独立集=总权-最小点权覆盖集

1 . 对于整个图,把坐标和(x+y)是偶数的点放在一堆,坐标和是奇数的放一堆;因为整个图,坐标和为偶数的任意两点都不是相邻的,坐标和为奇数的也一样;

我们把坐标和为偶数的点连源点,容量为数值本身;坐标和为奇数的连汇点,容量为数值本身;

对于每个点,于它上下左右四个点连边,这四个点都是和它相邻的,所以不能喝它共存;容量为inf ;

得到建好的网络后,我们就可以就对网络求最小割,因为被割的边和最小,题目求最大,所以结果是 sum-sap();

 

#include<cstdio>
#include<cstring>
#include<map>
#include<vector>
#include<cmath>
#include<cstdlib>
#include<stack>
#include<queue>
#include <iomanip>
#include<iostream>
#include<algorithm>
using namespace std ;
const int N=450 ;
const int M=N*8 ;
const int inf = 1<<30;
struct node
{
	int u,v,c,next;
}edge[M];
int pre[N],head[N],dis[N],cur[N],gap[N];
int top ;
int move[4][2]={0, 1, 0, -1, 1, 0, -1, 0};

void add(int u ,int v,int c)
{
	edge[top].u=u;
	edge[top].v=v;
	edge[top].c=c;
	edge[top].next=head[u];
	head[u]=top++;
	edge[top].u=v;
	edge[top].v=u;
	edge[top].c=0;
	edge[top].next=head[v];
	head[v]=top++;
}

int sap(int s,int t,int n)
{
	int u,v,mindis,minflow=inf,flow=0;
	memset(gap,0,sizeof(gap));
	memset(dis,0,sizeof(dis));
	memset(pre,-1,sizeof(pre));
	for(int i=0;i<n;i++)  cur[i]=head[i] ;
	u=pre[s]=s;
	gap[0]=n;
	while(dis[s] <n)
	{
		   loop:
		   	 for(int &i=cur[u] ; i!=-1 ; i=edge[i].next)
		   	 {
		   	 	   v=edge[i].v;
		   	 	   if(edge[i].c && dis[u]==dis[v]+1)
		   	 	   {
		   	 	   	    pre[v]=u;
		   	 	   	    u=v;
		   	 	   	    if(minflow > edge[i].c)
		   	 	   	      minflow = edge[i].c ;
		   	 	   	    if(v==t)
		   	 	   	    {
		   	 	   	    	for(u=pre[v] ; v!=s ; v=u,u=pre[u])
		   	 	   	    	{
		   	 	   	    		edge[cur[u]].c -=minflow ;
		   	 	   	    		edge[cur[u]^1].c += minflow ;
		   	 	   	    	}
		   	 	   	    	flow +=minflow ;
		   	 	   	    	minflow=inf;
		   	 	   	    }
		   	 	   	   goto loop ;
		   	 	   }
		   	 }
		   
		   mindis=n;
		   for(int i=head[u];i!=-1 ; i=edge[i].next)
		   {
		   	    v=edge[i].v;
		   	   if(edge[i].c>0 && dis[v] < mindis)
		   	   {
		   	   	     mindis=dis[v];
		   	   	     cur[u]=i;
		   	   }
		   }
		   if((--gap[dis[u]]) ==0)  break;
		   gap[dis[u]=mindis+1]++;
		   u=pre[u];
	}
	return flow ;
}

int main()
{
	int n ,x ,sum;
	while(~scanf("%d",&n))
	{
	       top=0;sum=0;
	       memset(head,-1,sizeof(head));
	       int s=0,t=n*n+1 ;
	       for(int i = 1 ; i <= n ; i++)
	         for(int j = 1 ; j <= n ; j++)
	          {
	          	    int tmp=(i-1)*n+j ;
					scanf("%d",&x);
	          	    if((i+j)%2==0)        //坐标和为偶数 
	          	       add(s,tmp,x);
	          	    else
	          	       add(tmp,t,x)   ;
	          	       sum+=x;
	          }
	      for(int i = 1 ; i <= n ; i++)
	         for(int j = 1 ; j <= n ; j++)
	           if((i+j)%2==0)                //坐标和为偶数的点跟周围的点建边就可以把所有点都建完了; 
	           {
	        	      int tmp=(i-1)*n+j;
	        	      if(i>1)   add(tmp,tmp-n,inf) ;     //判断边界; 
                      if(i<n)   add(tmp,tmp+n,inf) ;   	        	
	        	      if(j<n)   add(tmp,tmp+1,inf) ;
	        	      if(j>1)   add(tmp,tmp-1,inf) ;	        	      
	           }
	         int ans=sap(s,t,t+1);
			 printf("%d\n",sum-ans) ;  
	}
	 return 0;
}


 

 

你可能感兴趣的:(hdu 1565 方格取数(1) (最小割/状态压缩+DP))