状态压缩DP题目小节(二)

最近做的状态压缩DP小节:


http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=4257

zoj 4257

一堆气体相互碰撞产生能量,求最后能产生的最大能量,应该是很基础的状态压缩DP吧,设dp[flag]表示状态flag时能产生的最大能量,(flag中1表示该气体还存在,0表示该气体已经消失)边界条件是flag所有位都为一时,这时产生的能量为0,然后就枚举最后剩下的气体,求最大值即可。

 

#include <iostream>

#include <string.h>

#include <stdio.h>

#include <algorithm>

using namespace std;

int dp[1<<10];

int bo[11][11];

int n;

int max(int a,int b)

{

    return a>b?a:b;

}

int dfs(int flag)

{

    if(dp[flag]!=-1)

    return dp[flag];

    int i,j;

    int ans=0;

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

    {

        if(flag&1<<(i-1))

        {

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

            {

                if(j!=i&&(flag&1<<(j-1))==0)

                {

                    ans=max(ans,dfs(flag^(1<<(j-1)))+bo[i][j]);

                }

            }

        }

    }

    return dp[flag]=ans;

}

int main()

{

    //freopen("dd.txt","r",stdin);

    while(scanf("%d",&n)&&n)

    {

        int i,j;

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

        {

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

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

        }

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

        int ans=0;

        dp[(1<<n)-1]=0;

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

        {

            ans=max(ans,dfs(1<<i));

        }

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

    }

    return 0;

}


 

http://acm.hdu.edu.cn/showproblem.php?pid=3001

hdu 3001:

类似于TSP问题,但是这里不同的是每个点最多可以走两次,而不是一次,所以我们可以用三进制来表示每一个状态,我们设dp[now][flag]表示目前在now点状态为flag时的最小花费,则边界条件为当(1<<(now-1))==flag时,这时表示起点在now点,还没开始走,则此时的最小花费是0,还有一个边界条件为(1<<(now-1))*2==flag,这表示从now点走到now点,这是不可能的,所以把最小花费设为无穷大,剩下的就是状态转移了。

dp[now][flag]=dp[pre][flag']+map[pre][now],满足以下几个条件:

1:pre!=now且map[pre][now]不为-1(也就是存在边)

2:flag的第pre位不为0

3:flag'的第now位比flag的第now位少1.

我们求最小值即可,我们要枚举所有每一位都不为0的状态,取它们的最小值即为答案。

 

#include <iostream>

#include <string.h>

#include <stdio.h>

#include <algorithm>

#define inf 2100000000

using namespace std;

int bo[11][11];

int dp[11][60000];

int bit[60000][11];

int pow[12];

void init()

{

    int i,j;

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

    {

        int t=i;

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

        {

            bit[i][j]=t%3;

            t/=3;

        }

    }

    pow[1]=1;

    for(i=2;i<=11;i++)

    pow[i]=pow[i-1]*3;

}

int check(int x)

{

    while(x)

    {

        if(x%3==0)

        return 0;

        x/=3;

    }

    return 1;

}

int n,m;

int min(int a,int b)

{

    return a<b?a:b;

}

int dfs(int now,int flag)

{

    if(dp[now][flag]!=-1)

    return dp[now][flag];

    if(pow[now]==flag)

    return dp[now][flag]=0;

    if(pow[now]*2==flag)

    return dp[now][flag]=inf;

    int ans=inf,i;

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

    {

        if(i!=now&&bit[flag][i]&&bo[i][now]!=-1)

        {

            int tmp=dfs(i,flag-pow[now]);

            if(tmp!=inf)

            {

                ans=min(ans,tmp+bo[i][now]);

            }

        }

    }

    return dp[now][flag]=ans;

}

int main()

{

   // freopen("dd.txt","r",stdin);

    init();

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

    {

        int i,a,b,c;

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

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

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

        {

            scanf("%d%d%d",&a,&b,&c);

            if(bo[a][b]==-1||bo[a][b]>c)

            bo[a][b]=bo[b][a]=c;

        }

        int ma=pow[n+1]-1,mi=ma/2;

        int ans=inf;

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

        {

            for(int j=mi;j<=ma;j++)

            {

                if(check(j))

                ans=min(ans,dfs(i,j));

            }

        }

        if(ans==inf)

        ans=-1;

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

    }

    return 0;

}

 


http://poj.org/problem?id=3311
poj 3311

也是类似于TSP问题,不过这个时候每个点可以走无限次,所以我们要用依次floyd算法求一下个点之间的最短路,这里设dist[u][v]表示u和v之间的最短路,然后就和平常求TSP问题一样了,我们还是设dp[now][flag]表示当前在now点状态为flag的最小花费,边界条件为flag==(1<<(now-1))时,此时表示从起点开始走第一次到达到达now,为其他店还没开hi走,则dp[now][flag]=dist[0][now],其他情况基本和上体一样。

注意题目要求最后一定要回到0点。

 

#include <iostream>

#include <string.h>

#include <algorithm>

#include <stdio.h>

#define inf 2100000000

using namespace std;

int map[11][11];

int dp[11][1<<10];

int n;

int min(int a,int b)

{

    return a<b?a:b;

}

int dfs(int now,int flag)

{

    //printf("%d %d\n",now, flag);

    if(dp[now][flag]!=-1)

    return dp[now][flag];

    int i,j;

    if(flag==(1<<(now-1)))

    return dp[now][flag]=map[0][now];

    int ans=inf;

    if(now==0)

    {

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

        {

            ans=min(ans,dfs(i,flag)+map[i][now]);

        }

    }

    else

    {

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

        {

            if(i!=now&&flag&(1<<(i-1)))

            {

                ans=min(ans,map[i][now]+dfs(i,flag^(1<<(now-1))));

            }

        }

    }

    return dp[now][flag]=ans;

}

void floyd(int n)

{

    int i,j,k;

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

    {

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

        {

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

            {

                map[j][k]=min(map[j][k],map[j][i]+map[i][k]);

            }

        }

    }

}

int main()

{

    //freopen("dd.txt","r",stdin);

    int i,j;

    while(scanf("%d",&n)&&n)

    {

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

        {

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

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

        }

        floyd(n);

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

        dp[0][0]=0;

        printf("%d\n",dfs(0,(1<<n)-1));

    }

    return 0;

}


poj 2288

 

还是类似于TSP问题,不过这里对于花费做了新的定义,花费分为三部分

1:路径中每个点的权值之和。

2:路径中相邻两个点的权值之积的和。

3:若存在路径pi->pi+1->pi+2,且pi和pi+2之间有边,则花费还要加上这三点的权值之和。

我们要求花费最大的路径以及这样的路径的个数

所以我们社状态时不能只考虑当前点,还要考虑之前的点了,所以我们设dp[now][pre][flag]表示当前点在now,上一个点在pre,此时状态为flag时的最大花费。设way[now][flag][flag]为dp[now][pre][flag]取最大值时的路径条数。则接下来我们就和求TSP问题时差不多了,只是求花费时稍微麻烦一点,边界条件是当now为起点时,即(1<<(now-1))==flag时,此时花费为now点的权值,此时的路径条数为1.代码还有一些细节,具体实现请参考代码。

 

#include <iostream>

#include <string.h>

#include <algorithm>

#include <stdio.h>

#define maxn 100010

#define ll long long

using namespace std;

ll dp[14][14][1<<13];

ll way[14][14][1<<13];

int bo[14][14];

int v[14];

void init()

{

    memset(bo,0,sizeof(bo));

    memset(way,0,sizeof(way));

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

}

ll max(ll a,ll b)

{

    return a>b?a:b;

}

int n;

ll dfs(int now,int pre,int flag)

{

   // printf("f");

    if(dp[now][pre][flag]!=-1)

    return dp[now][pre][flag];

    if(1<<(now-1)==flag)

    {

        way[now][pre][flag]=1;

        return dp[now][pre][flag]=v[now];

    }

    int i,tru=0;

    ll ans=0;

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

    {

        if(i!=now&&i!=pre&&(flag&(1<<(i-1))))

        {

            tru=1;

            if(bo[i][pre])

            {

                ll tmp=0;

                tmp=dfs(pre,i,flag^(1<<(now-1)));

                if(tmp)

                {

                    tmp+=v[now]+v[now]*v[pre];

                    if(bo[i][now])

                    tmp+=v[now]*v[pre]*v[i];

                    ans=max(ans,tmp);

                }

            }

        }

    }

    if(!tru)

    {

        dp[now][pre][flag]=dfs(pre,0,flag^(1<<(now-1)))+v[now]+v[now]*v[pre];

        way[now][pre][flag]=1;

        return dp[now][pre][flag];

    }

    if(ans)

    {

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

        {

            if(i!=now&&i!=pre&&bo[i][pre]&&(flag&(1<<(i-1))))

            {

                ll tmp=0;

                tmp=dfs(pre,i,flag^(1<<(now-1)));

                if(tmp)

                {

                    tmp+=v[now]+v[now]*v[pre];

                    if(bo[i][now])

                    tmp+=v[now]*v[pre]*v[i];

                    if(ans==tmp)

                    {

                        way[now][pre][flag]+=way[pre][i][flag^(1<<(now-1))];

                    }

                }

            }

        }

    }

    return dp[now][pre][flag]=ans;

}

int main()

{

    //freopen("dd.txt","r",stdin);

    int ncase;

    scanf("%d",&ncase);

    while(ncase--)

    {

        int m,i,a,b,j;

        init();

        scanf("%d%d",&n,&m);

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

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

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

        {

            scanf("%d%d",&a,&b);

            bo[a][b]=bo[b][a]=1;

        }

        ll ans=0,num=0;

        if(n==1)

        printf("%d 1\n",v[1]);

        else

        {

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

            {

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

                {

                    if(i!=j&&bo[i][j])

                    {

                        dfs(i,j,(1<<n)-1);

                        ans=max(ans,dp[i][j][(1<<n)-1]);

                    }

                }

            }

            if(ans==0)

            printf("0 0\n");

            else

            {

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

                {

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

                    {

                        if(i!=j&&bo[i][j]&&dp[i][j][(1<<n)-1]==ans)

                        {

                            num+=way[i][j][(1<<n)-1];

                        }

                    }

                }

                printf("%I64d %I64d\n",ans,num/2);

            }

        }

    }

    return 0;

}


 


 

你可能感兴趣的:(压缩)