hdu 3657 最大点权独立集变形(方格取数的变形最小割,对于最小割建图很好的题)

转载:http://blog.csdn.net/cold__v__moon/article/details/7924269
/*
这道题和方格取数2相似,是在方格取数2的基础上的变形。

方格取数2解法:
    由题意知对于每一个方格,有选与不选,显然是二分的最大独立集,先求最小点权覆盖(它的补集恰好
	是最大点权独立集),对于任何一条可行流 s->u->v->t, 在求最大流或最小割的时候,在这3条边中
	至少选一条,将u->v设为inf,u->v就不可能存在于最小割中,就只是2选1,如果s->u或v->t选为最小割
	则表示u或v(u和v可同时被选)属于最小点权覆盖,所以最小割=最小点权覆盖,再求补集就是最大点权独立集。

这道题的变形:
    先考虑的答案的补集(类似最小点权覆盖,但不是),和方格取数不同的是可以去相邻的格子但是要减去2*(G[i][j]&G[a][b]);
	所以对于答案的补集就是加上2*(G[i][j]&G[a][b]),对于任何一条可行流 s->u->v->t, 当u->v为2*(G[i][j]&G[a][b]),就可以
	表示同时选取u,v的情况,对于答案的必选点s->u就是不存在割的s->u为inf;
推荐学习胡伯涛《最小割模型在信息学竞赛中的应用》,其中对最小割的模型应用讲的很清楚
*/
#include <iostream>
#include <memory.h>
#include <stdio.h>
using namespace std;
const int maxn=55;
const int maxm=maxn*maxn;
const int inf=0x3fffffff;
int G[maxn][maxn];
bool f[maxn][maxn];
int head[maxm],dis[maxm],gap[maxm],cur[maxm],e,pre[maxm];
int dir[4][2]={0,1,0,-1,1,0,-1,0};
int source,sink;
struct
{
	int t,next,w;
}edge[maxm*6];
void add(int a,int b,int c)
{
    //printf("%d->%d %d
",a,b,c);
    edge[e].t=b;
    edge[e].w=c;
    edge[e].next=head[a];
    head[a]=e++;
	edge[e].t=a;
    edge[e].w=0;
    edge[e].next=head[b];
    head[b]=e++;
}
int sap(int ncnt)
{
    memset(dis,0,sizeof(dis));
    memset(gap,0,sizeof(gap));
    for(int i=0;i<=ncnt;i++)
        cur[i]=head[i];
    int u=pre[source]=source,maxflow=0,aug=inf;
    gap[0]=ncnt;
    //P();
    while(dis[source]<ncnt)
    {
loop:    for(int &i=cur[u];i!=-1;i=edge[i].next)
        {
            //P();
            int v=edge[i].t;
            if(edge[i].w&&dis[u]==dis[v]+1)
            {
                aug=min(aug,edge[i].w);
                pre[v]=u;
                //printf("%d-> %d
",u,v);
                u=v;
                if(v==sink)
                {
                    maxflow+=aug;
                    for(u=pre[u];v!=source;v=u,u=pre[u])
                    {
                        edge[cur[u]].w-=aug;
                        edge[cur[u]^1].w+=aug;
                        //printf("d");
                    }
                    aug=inf;
                }
                goto loop;
            }
		 }
         int mindis=ncnt;
            //P();
            for(int i=head[u];i!=-1;i=edge[i].next)
            {
                int v=edge[i].t;
                if(edge[i].w&&mindis>dis[v])
                {
                    //printf("s");
                    cur[u]=i;
                    mindis=dis[v];
                }
            }

            if(--gap[dis[u]]==0)
                break;
            gap[dis[u]=mindis+1]++;
            u=pre[u];
        
    }

    return maxflow;
}
int main()
{
	int n,m,a,b,sum,k;
	while(~scanf("%d%d%d",&n,&m,&k))
	{
		sum=0;
		for(int i=0;i<n;i++)
			for(int j=0;j<m;j++)
			{
				scanf("%d",&G[i][j]);
				sum+=G[i][j];
			}
		memset(f,false,sizeof(f));
		for(int i=0;i<k;i++)
		{
			scanf("%d%d",&a,&b);
			a--;
			b--;
			f[a][b]=true;
		}
		memset(head,-1,sizeof(head));
		e=0;
		source=n*m;
		sink=n*m+1;
		for(int i=0;i<n;i++)
			for(int j=0;j<m;j++)
				if((i+j)%2==1)
				{
					add(source,i*m+j,f[i][j]?inf:G[i][j]);
					for(int k=0;k<4;k++)
					{
						a=i+dir[k][0];
						b=j+dir[k][1];
						if(a>=0&&a<n&&b>=0&&b<m)
							add(i*m+j,a*m+b,(G[i][j]&G[a][b])<<1);
					}
				}
				else add(i*m+j,sink,f[i][j]?inf:G[i][j]);
		printf("%d
",sum-sap(n*m+2));

	}

	return 0;
}

原文地址:https://www.cnblogs.com/thefirstfeeling/p/4410659.html