hdu 3657 Game (最小割)

大致题意:
    给出一个n*m的矩阵,让你从中取出一定数量的数字。如果在矩阵中两两相邻的数字被取到的话需要付出一定的代价。而且给出某些点,规定这些点一定需要取到。求最多可以取到多少点。
 
大致思路:
    怎么说呢,这道题乍看上去和hdoj 1569:方格取数很相似,也很像是一个二分图的最大点权独立集问题。但是问题出的很巧妙,也就没有办法往模版上面套了。把矩阵中的点按照横纵坐标之和的奇偶性分成两个集合,设超级源汇点,源点第一个集合中的所有点连边,容量为这个点代表的数字的值。第二个集合中的所有点向汇点连边,容量也是这个点的值。第一个集合中点都向他周围的点连边,容量为他们同时被取时的消耗。如果一个点必须取,那就将他和源/汇点的容量设为inf,保证这条边不被割掉。用所有点的权值之和sum减去这个图的最小割得到的就是答案。总的来说,ac后的感受就是,这是一道需要意识流的题目
 
#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=100001 ;
const int M=N*5 ;
const int inf=1<<30 ;
struct node
{
	int u,v,c,next;
}edge[M];
int head[N],gap[N],dis[N],cur[N],pre[N];
int top ;

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

int main()
{
	int n,m,k,a,b ;
	int x[100][100];
	while(~scanf("%d%d%d",&n,&m,&k))
	{
		    top=0;
		    memset(head,-1,sizeof(head));
		    int s=0,t=n*m+1 ,sum=0;
		    for(int i=1;i<=n;i++)
		      for(int j=1;j <=m; j++)
		       {
		       	       scanf("%d",&x[i][j]);
		       	       int tmp=(i-1)*m+j;
		       	       if((i+j)%2==0)             //连源点汇点 
		       	       	    add(s,tmp,x[i][j]);
		       	       else
		       	           add(tmp,t,x[i][j]);
		       	         sum += x[i][j];
		       }
		     for(int i=1;i<=k;i++)
			 {
			 	  scanf("%d%d",&a,&b);
			 	  int tmp=(a-1)*m+b;
			 	  if((a+b)%2==0)              //规定的点,容量改为inf,避免被割, 
		       	        add(s,tmp,inf);
		       	   else
		       	        add(tmp,t,inf);		       	       
			 }  
		     for(int i=1;i<=n;i++)
			   for(int j=1;j<=m;j++)
			    if((i+j)%2==0)
				{
			    	   int tmp=(i-1)*m+j;      //相邻损失的分数为 2(a&b); 
		       	       if(i>1)  add(tmp,tmp-m,2*(x[i][j]&x[i-1][j])) ;  
		       	       if(i<n)  add(tmp,tmp+m,2*(x[i][j]&x[i+1][j])) ;
		       	       if(j>1)  add(tmp,tmp-1,2*(x[i][j]&x[i][j-1])) ;
		       	       if(j<m)  add(tmp,tmp+1,2*(x[i][j]&x[i][j+1])) ;			    	
			    }  
			  int ans=sap(s,t,t+1);
			  printf("%d\n",sum-ans) ;
	}
	return 0;
}
 

 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值