hdu1565 网络流或状态压缩DP

对于网络流有一个定理:

最小点权覆盖集=最大网络流;

最大点权独立集=总权值-最小点权覆盖集;

网络流解法代码如下:

#include<cstdio>

#include<iostream>

#include<cstring>

#include<algorithm>

#define N 1010

#define M 50010

#define inf 1<<30

using namespace std;

struct Edge{

    int to,val,next;

}edge[M];

int index[N],d[N],gap[N],e;

void addedge(int from,int to,int val)

{

    edge[e].to=to;

    edge[e].val=val;

    edge[e].next=index[from];

    index[from]=e++;

    edge[e].to=from;

    edge[e].val=0;

    edge[e].next=index[to];

    index[to]=e++;

}

int source,des,n,m;

//n is the number of point

int dfs(int pos,int flow)

{

    if(pos==des)

        return flow;

    int i,j,v,val,lv,mind,c;

    mind=n-1;//初始最小标号为n-1

    lv=flow;

    for(i=index[pos];i!=-1;i=edge[i].next)

    {

        v=edge[i].to;

        val=edge[i].val;

        if(val)

        {

            if(d[v]+1==d[pos])

            {

                c=min(lv,val);//对于该点的最小可行流

                c=dfs(v,c);

                edge[i].val-=c;//更新剩余图

                edge[i^1].val+=c;

                lv-=c;

                if(d[source]>=n)return flow-lv;

                if(lv==0) break;

            }

            if(d[v]<mind)mind=d[v];//找出与pos相连的点的最小标号

        }

    }

    if(lv==flow)//没有找到增广路劲,进行标号更新

    {

        --gap[d[pos]];

        if(!gap[d[pos]])

            d[source]=n;

        d[pos]=mind+1;

        ++gap[d[pos]];

    }

    return flow-lv;

}

int sap(int st,int de)

{

    source=st;

    des=de;

    memset(d,0,sizeof(d));

    memset(gap,0,sizeof(gap));

    gap[0]=n;//初始标号为0的有n个.

    int ans=0;

    while(d[st]<n)

    {

        ans+=dfs(st,inf);

        //cout<<d[st]<<endl;

    }

    return ans;

}

void init()

{

    e=0;

    memset(index,-1,sizeof(index));

}

int pos(int a,int b)

{

    return (a-1)*m+b;

}

int main()

{

    int t,i,j;

    while(scanf("%d",&m)!=EOF)

    {

        

        init();

        int w;

        int sum=0;

        for(i=1;i<=m;i++)

            for(j=1;j<=m;j++)

            {

                scanf("%d",&w);

                sum+=w;

                if((i+j)%2==0)

                {

                    addedge(0,pos(i,j),w);

                    if(i<m)

                        addedge(pos(i,j),pos(i+1,j),inf);

                    if(j<m)

                        addedge(pos(i,j),pos(i,j+1),inf);

                    if(i>1)

                        addedge(pos(i,j),pos(i-1,j),inf);

                    if(j>1)

                        addedge(pos(i,j),pos(i,j-1),inf);

                }

                else

                    addedge(pos(i,j),m*m+1,w);

            }

            n=m*m+2;

        printf("%d\n",sum-sap(0,n-1));

    }

    return 0;

}

 状态压缩解法:

#include<iostream>

#include<cstdio>

#include<cstring>

#include<algorithm>

#include<cmath>

using namespace std;

int s[1<<21];

int map[22][22];

int dp[2][1<<21];

int main()

{

    int n,i,j,t,k,num=0;;

    n=1<<21;

    for(i=0;i<=n;i++)//记录可行状态

        if((i&(i<<1))==0)

            s[num++]=i;

    while(scanf("%d",&n)!=EOF)

    {

        for(i=0;i<n;i++)

            for(j=0;j<n;j++)

                scanf("%d",&map[i][j]);

        int p=0;

        memset(dp,0,sizeof(dp));

        for(i=0;i<n;i++)//枚举每一行

        {

            p^=1;//进行滚动数组

            for(j=0;j<num;j++)//枚举每行的所有状态

            {

                if(s[j]>(1<<n))

                    break;

                int sum=0;

                for(k=0;k<n;k++) if(s[j]&(1<<k)) sum+=map[i][k];//记录该状态值

                for(k=0;k<num;k++)//枚举已经得到的状态转移到该状态能到的最大值

                {

                    if(s[k]>(1<<n))

                        break;

                    if(!(s[k]&s[j]))

                        dp[p][s[j]]=max(dp[p][s[j]],dp[1-p][s[k]]+sum);

                }

            }

        }

        int ans=0;

        for(i=0;i<num&&s[i]<=(1<<n);i++)//寻找答案

            ans=max(ans,dp[p][s[i]]);

        printf("%d\n",ans);

    }

    return 0;

}

 

你可能感兴趣的:(HDU)