zoukankan      html  css  js  c++  java
  • 状态压缩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;
    }
    



  • 相关阅读:
    ansible使用
    git undo last commit
    metadata简介
    tinyint(4),tinyint(80)有什么区别
    php 打印debug日志
    tinycore Network card configuration during exec bootlocal.sh
    Windows使用CMD命令查看进程和终止进程
    @NotEmpty,@NotNull和@NotBlank的区别
    spring boot 程序启动缓慢的问题(二)
    LocalDateTime的一些用法
  • 原文地址:https://www.cnblogs.com/xinyuyuanm/p/2999158.html
Copyright © 2011-2022 走看看