首页 > 代码库 > HDU1565 方格取数1(构图+网络流最大独立集合)

HDU1565 方格取数1(构图+网络流最大独立集合)

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

解题思路:最大点权独立集,关键是怎么建图了,我们可以采用染色的思想对这张图进行染色,然后分成两个点集 
假设将第一个格子染成白色,然后将它相邻的格子染成相反的颜色黑色,以此类推,这样就可以将一张图分成染成黑白两种颜色的点集了 
然后就是连边了,连边的话,我们只考虑白色格子的连向黑色格子的,因为两者之间是相对的,所以只需要取一条就好了 
这样图就建好了 
最大独立集就是:总权值-最小割了(最小割就是最小点权覆盖了)

最小割即最大流

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#include<string>
#include<vector>
#include<set>
#include<map>
#include<queue>
using namespace std;
int n,a[21][21];
#define MAXN 500
#define MAXE 1100000
#define INF 0x7fffffff
int ne,nv,s,t;
int size,net[MAXN];
struct EDGE{
    int v,next;
    int cap;
    int flow;
}edge[MAXE];

void init(){
    size=0;
    memset(net,-1,sizeof(net));
}
void add(int u,int v,int cap){
    //cout<<u<<" "<<v<<" "<<cap<<endl;
    ne++;
    edge[size].v = v;
    edge[size].cap = cap;
    edge[size].flow = 0;
    edge[size].next = net[u];
    net[u] = size;
    ++size;
    
    edge[size].v = u;
    edge[size].cap = 0;
    edge[size].flow = 0;
    edge[size].next = net[v];
    net[v] = size;
    ++size;
}

int gap[MAXN];//gap优化
int dist[MAXN];//距离标号
int pre[MAXN];//前驱
int curedge[MAXN];//当前弧

int ISAP(){
    int cur_flow,u,temp,neck,i;
    int max_flow;
    memset(gap,0,sizeof(gap));
    memset(pre,-1,sizeof(pre));
    memset(dist,0,sizeof(dist));
    for(i=1;i<=nv;i++) curedge[i]=net[i];//将当前弧初始话成邻接表的第一条边
    gap[nv]=nv;
    max_flow=0;
    u=s;
    while(dist[s]<nv){
        if(u==t){//找到一条增广路
            cur_flow=INF;
            for(i=s;i!=t;i=edge[curedge[i]].v){//沿着增广路找到最小增广流量
                if(cur_flow>edge[curedge[i]].cap){
                    neck=i;
                    cur_flow=edge[curedge[i]].cap;
                }
            }
            for(i=s;i!=t;i=edge[curedge[i]].v){//更新
                temp=curedge[i];
                edge[temp].cap-=cur_flow;
                edge[temp].flow+=cur_flow;
                temp^=1;
                edge[temp].cap+=cur_flow;
                edge[temp].flow-=cur_flow;
            }
            max_flow+=cur_flow;
            u=neck;//下次直接从关键边的u开始新一轮的增广
        }
        for(i=curedge[u];i!=-1;i=edge[i].next)//找到一条允许弧
            if(edge[i].cap>0&&dist[u]==dist[edge[i].v]+1)
                break;
        if(i!=-1){//如果找到 将u指向v
            curedge[u]=i;
            pre[edge[i].v]=u;
            u=edge[i].v;
        }
        else{//找不到
            if(0==--gap[dist[u]]) break;//出现断层
            curedge[u] = net[u];//把当前弧重新设为邻接表中满足要求的第一条弧
            for(temp=nv,i=net[u];i!=-1;i=edge[i].next)
                if(edge[i].cap > 0)
                    temp=temp<dist[edge[i].v]?temp:dist[edge[i].v];
            dist[u]=temp+1;//将这个点的距离标号设为由它出发的所有弧的终点的距离标号的最小值加1
            ++gap[dist[u]];
            if(u!=s)u=pre[u];
        }
    }
    //cout<<max_flow<<endl;
    return max_flow;
}
int main()
{
    while (~scanf("%d",&n))
    {
        init();
        int i,j,sum=0;
        for (i=1;i<=n;i++)
            for (j=1;j<=n;j++) scanf("%d",&a[i][j]),sum+=a[i][j];
        ne=0;
        nv=n*n+2;
        s=1;
        t=n*n+2;
        for (i=1;i<=n;i++)
            for (j=1;j<=n;j++)
                if ((i+j)&1) add(n*(i-1)+j+1,n*n+2,a[i][j]); else
                {
                    add(1,n*(i-1)+j+1,a[i][j]);
                    if (i>1) add(n*(i-1)+j+1,n*(i-2)+j+1,(1<<30)-1);
                    if (i<n) add(n*(i-1)+j+1,n*i+j+1,(1<<30)-1);
                    if (j>1) add(n*(i-1)+j+1,n*(i-1)+j,(1<<30)-1);
                    if (j<n) add(n*(i-1)+j+1,n*(i-1)+j+2,(1<<30)-1);
                 }
        //cout<<nv<<" "<<ne<<endl;
        printf("%d\n",sum-ISAP());
     }
    return 0;
 }

 

HDU1565 方格取数1(构图+网络流最大独立集合)