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;
}

 

posted @ 2013-07-08 18:38  fangguo  阅读(257)  评论(0编辑  收藏  举报