• POJ3254:Corn Fields(状压dp第一发)


    题目:http://poj.org/problem?id=3254

    直接上代码吧,刚开始做时主要的问题就是看不懂二进制,有个博客写的太好了,就直接把题解复制在下面了。

    #include <iostream>
    #include <stdio.h>
    #include <string.h>
    #include <algorithm>
    #include <math.h>
    #define mod 100000000
    
    int m,n,state[1010],dp[20][1010],top,cur[20];
    inline bool ok(int x)
    {
        if(x&x<<1) return false;
        return true;
    }
    void init()
    {
        int total=1<<n;
        top=0;
        for(int i=0;i<total;i++)
        {
            if(ok(i)) state[++top]=i;
        }
    }
    inline bool fit(int x,int k)
    {
        if(cur[k]&x) return false;
        return true;
    }
    int main()
    {
        int xx;
        while(scanf("%d%d",&m,&n)!=EOF)
        {
            init();
            for(int i=1;i<=m;i++)
            {
                cur[i]=0;
                for(int j=1;j<=n;j++)
                {
                    scanf("%d",&xx);
                    if(xx==0) cur[i]+=(1<<(n-j));
                }
            }
            memset(dp,0,sizeof(dp));
            for(int i=1;i<=top;i++)
            {
                if(fit(state[i],1))
                    dp[1][i]=1;
            }
            for(int i=2;i<=m;i++)
            {
                for(int k=1;k<=top;k++)
                {
                    if(!fit(state[k],i)) continue;
                    for(int j=1;j<=top;j++)
                    {
                        if(!fit(state[j],i-1)) continue;
                        if(state[j]&state[k]) continue;
                        dp[i][k]=(dp[i][k]+dp[i-1][j])%mod;
                    }
                }
            }
            int ans=0;
            for(int i=1;i<=top;i++)
            {
                ans=(ans+dp[m][i])%mod;
            }
            printf("%d
    ",ans);
        }
        return  0;
    }

    题目大意:农夫有一块地,被划分为m行n列大小相等的格子,其中一些格子是可以放牧的(用1标记),农夫可以在这些格子里放牛,其他格子则不能放牛(用0标记),并且要求不可以使相邻格子都有牛。现在输入数据给出这块地的大小及可否放牧的情况,求该农夫有多少种放牧方案可以选择(注意:任何格子都不放也是一种选择,不要忘记考虑!

    解题思路:以样例数据第一行为例,三个格子都可以放牧,即每个格子都可以选择放,或不放。再考虑附加条件“相邻格子不可同时放牧”,那么我们可以列出单看第一行时的所有可行状态如下(1代表放牧,0代表不放牧)

    编号 状态
    1 0 0 0 
    2 0 0 1
    3 0 1 0
    4 1 0 0
    5 1 0 1
    (表1)

    由此,可将表中的状态看作二进制表示,那么,只需将每种状态转化为相应的十进制数,即可只用一个数字,就能表示某一种状态,如下表:

    编号 二进制 十进制
    1 0 0 0 0
    2 0 0 1 1
    3 0 1 0 2
    4 1 0 0 4
    5 1 0 1 5
    (表2)

    这种用一个数来表示一组数,以降低表示状态所需的维数的解题手段,就叫做状态压缩。

    至此我们看到,在只考虑第一行的时候,有5种可行的放牧方案,但这只是我们要做的第一步。接下来要将第二行纳入考虑:

    首先思考:纳入第二行后,会对当前问题造成什么样的影响?

    答案还是那句话:“相邻格子不可同时放牧”!

    也就是说,不止左右相邻不可以,上下之间也不能存在相邻的情况。

    首先观察第二行,只有中间的格子可以放牧,那么我们的状态表格就可以相对简单些了~如下:

    编号 二进制 十进制
    1 0 0 0 0
    2 0 1 0 2
    (表3)


    只有两种可行状态,那么我们不妨一个一个来考察:

    1、当第二行的状态为编号1时,第二行的三个格子都没有放牧,那么就不会与第一行的任何情况有冲突,第一行的5种方案都可行,即:第二行选用编号1的状态时,结合第一行,可得到5种可行的放牧方案;

    2、当第二行的状态为编号2时,第二行中间的格子已经放牧了,那么第一行中间的格子就不可以放牧。看表2,发现其中第3种状态与当前第二行冲突,那么第一行只有4种方案是可行的,即:第二行选用编号2的状态时,结合第一行,可得到4种可行的放牧方案;

    那么,在样例数据给出的情况下,我们的最终答案即为5+4=9;

    通过对样例数据的分析即可以发现不同状态之间的关系:

    以dp[i][state(j)]来表示对于前i行,第i行采用第j种状态时可以得到的可行方案总数!

    例如:回头看样例数据,dp[2][1]即代表第二行使用第2中状态(0 1 0)时可得的方案数,即为4;

    那么,可得出状态转移方程为:

    dp[i][state(j)]=dp[i-1][state(k1)]+dp[i-1][state(k2)]+......+dp[i-1][state(kn)](kn即为上一行可行状态的编号,上一行共有n种可行状态)

    最终ans=dp[m][state(k1)]+dp[m][state(k2)]+......+dp[m][state(kn)]; (kn即为最后一行(第m行)可行状态的编号)

    程序代码:

    先声明,这道题我是参考了@AcCry的博文 “状态压缩DP总结【POJ3254】【POJ1185】【POJ3311】【HDU3001】【POJ2288】【ZOJ4257】【POJ2411】【HDU3681】”解出的,原文的讲解比较精简,我单独拿出其中第一题详解作为入门。

    先帖原帖代码,我在读的过程中加了详细注释,并做了一些细节上的修改:

    #include <cstdio>
    #include <cstring>
    using namespace std;
    
    #define mod 100000000
    int M,N,top = 0;
    //top表示每行最多的状态数
    
    int state[600],num[110];  
    //state存放每行所有的可行状态(即没有相邻的状态
    //
    
    int dp[20][600];
    //dp[i][j]:对于前i行数据,每行有前j种可能状态时的解
    int cur[20];
    //cur[i]表示的是第i行整行的情况
    
    inline bool ok(int x){    //判断状态x是否可行
       if(x&x<<1)    return false;//若存在相邻两个格子都为1,则该状态不可行
       return true;
    }
    void init(){            //遍历所有可能的状态
       top = 0;
       int total = 1 << N; //遍历状态的上界
       for(int i = 0; i < total; ++i){
           if(ok(i))state[++top] = i;    
       }
    }
    inline bool fit(int x,int k){ //判断状态x 与第k行的实际状态的逆是否有‘重合’
       if(x&cur[k])return false; //若有重合,(即x不符合要求)
       return true;  //若没有,则可行
    }
    
    int main(){
        while(scanf("%d%d",&M,&N)!= EOF){
           init();
           memset(dp,0,sizeof(dp));
           for(int i = 1; i <= M; ++i){
               cur[i] = 0;
               int num;
               for(int j = 1; j <= N; ++j){  //输入时就要按位来存储,cur[i]表示的是第i行整行的情况,每次改变该数字的二进制表示的一位
                    scanf("%d",&num);  //表示第i行第j列的情况(0或1)
                   if(num == 0) //若该格为0    
                       cur[i] +=(1<<(N-j)); //则将该位置为1(注意要以相反方式存储,即1表示不可放牧
               }
           }
           for(int i = 1;i <= top;i++){
               if(fit(state[i],1)){  //判断所有可能状态与第一行的实际状态的逆是否有重合
                    dp[1][i] = 1;  //若第1行的状态与第i种可行状态吻合,则dp[1][i]记为1
               }
           
           }
    
           /*
           状态转移过程中,dp[i][k] =Sigma dp[i-1][j] (j为符合条件的所有状态)
            */
           for(int i = 2; i <= M; ++i){  //i索引第2行到第M行
               for(int k = 1; k <= top; ++k){ //该循环针对所有可能的状态,找出一组与第i行相符的state[k]
                    if(!fit(state[k],i))continue; //判断是否符合第i行实际情况
                    for(int j = 1; j <= top ;++j){ //找到state[k]后,再找一组与第i-1行符合,且与第i行(state[])不冲突的状态state[j]
                       if(!fit(state[j],i-1))continue;  //判断是否符合第i-1行实际情况
                       if(state[k]&state[j])continue;  //判断是否与第i行冲突
                       dp[i][k] = (dp[i][k] +dp[i-1][j])%mod;  //若以上皆可通过,则将'j'累加到‘k'上
                    }
               }
           }
           int ans = 0;
           for(int i = 1; i <= top; ++i){ //累加最后一行所有可能状态的值,即得最终结果!!!泥马写注释累死我了终于写完了!
               ans = (ans + dp[M][i])%mod;
           }
           printf("%d
    ",ans);
       }
    }
  • 相关阅读:
    如何突破单库性能瓶颈?
    高性能数据库表该如何设计?
    高性能索引该如何设计?(下)
    高性能索引该如何设计?(上)
    MySQL体系结构与存储引擎
    动态ViewPager导航页面
    ViewPager图片轮转带点的
    手动图片横向轮播
    安卓布局中下拉列表框的实现
    安卓中adapter的应用
  • 原文地址:https://www.cnblogs.com/zhangmingcheng/p/4372704.html
Copyright © 2020-2023  润新知