• poj 3254 Corn Fields (状压DP入门)


    Corn Fields
    Time Limit: 2000MS   Memory Limit: 65536K
    Total Submissions: 20694   Accepted: 10855

    Description

    Farmer John has purchased a lush new rectangular pasture composed of M by N (1 ≤ M ≤ 12; 1 ≤ N ≤ 12) square parcels. He wants to grow some yummy corn for the cows on a number of squares. Regrettably, some of the squares are infertile and can't be planted. Canny FJ knows that the cows dislike eating close to each other, so when choosing which squares to plant, he avoids choosing squares that are adjacent; no two chosen squares share an edge. He has not yet made the final choice as to which squares to plant.

    Being a very open-minded man, Farmer John wants to consider all possible options for how to choose the squares for planting. He is so open-minded that he considers choosing no squares as a valid option! Please help Farmer John determine the number of ways he can choose the squares to plant.

    Input

    Line 1: Two space-separated integers: M and N 
    Lines 2..M+1: Line i+1 describes row i of the pasture with N space-separated integers indicating whether a square is fertile (1 for fertile, 0 for infertile)

    Output

    Line 1: One integer: the number of ways that FJ can choose the squares modulo 100,000,000.

    Sample Input

    2 3
    1 1 1
    0 1 0

    Sample Output

    9

    Hint

    Number the squares as follows:
    1 2 3
      4  

    There are four ways to plant only on one squares (1, 2, 3, or 4), three ways to plant on two squares (13, 14, or 34), 1 way to plant on three squares (134), and one way to plant on no squares. 4+3+1+1=9.

    题目大意:

    用一个最大12x12的01矩阵表示土地,1表示这块土地肥沃,0表示这块土地贫瘠。肥沃的土地可以用来种草放牧。

    但是牛喜欢独自占领一块地的感觉,所以你选择种草的土地不能是相连的。

    如果不考虑一共在多少块土地上种草,问你一共有多少种种植方案。不种植也算一种方案。

    摘抄一下BlackStorm的cnblog(%%%),我基本就是按照他的方式处理的:

    对于0-1状态矩阵,自然而然会想到用状态压缩来做,把一行(也可以按列)的状态压缩成一个十进制数(行状态)。
    另种植or不种植也可以用0-1表示,并根据题目所说不能挨着种植,即这一行的某个位置种植了,下一行的同一位置就不能种植,可以知道两行的种植状态相位与要为0。
    另外说一个行种植状态有效,即相邻的格子是不能种植的,需要左移一位后与自身相位与为0,如果存在相邻种植的格子,则一定会保留位1,不可能得出0的结果,据此枚举出这些状态再进行判断。
    然而并不是每个有效的行种植状态对于每一行都有效,因为有的行存在一些位置是不能种植的,用0表示。为了方便判断和计算,我们考虑将行状态取反,即0表示肥沃,1表示不肥沃,这样只有当行种植状态和行状态相位与为0,这个种植状态才在该行有效,因为如果种在了不肥沃的格子上,相位与会保留位1,结果不为0。
    于是我们有以下设计思路:
    ①在读入时就将格子状态取反,压缩成行状态存到row[]数组里;
    ②枚举所有有效的种植状态,存到rec[]数组里,并将最大值存进去避免后面越界;
    ③先处理第一行,给dp一个基准:对于每个有效种植状态,如果在第一行也有效,计数1次;
    ④对于剩余的行,不仅要判断每个有效种植状态,还要判断两行的种植状态有没有冲突;
    ⑤对于最后一行,把每个种植状态的计数加起来,就是总的种植方法数。
    以及DP数组:dp[r][j]表示当第r行的种植状态为第j种状态时,现在玉米地的种植方案数。
    状态转移方程: dp[r][j] = dp[r-1][i] + dp[r][j]if row[i-1]&rec[i]=0 and row[i]&rec[j]=0 andrec[i]&rec[j]=0.
    即rec[i]是row[i-1]的有效行状态,且rec[j]是row[r]的有效行状态,且rec[i]和rec[j]两个行状态不发生冲突。
     
    #include<cstdio>
    #include<queue>
    #include<cstring>
    #include<algorithm>
    
    typedef long long lol;//宏定义上线
    
    using namespace std;
    
    const int maxn=12;
    const int mod=100000000;
    
    int sat[400];//满足条件的状态(左移与为0),经验证本题只需要前400个
    
    int row[maxn+5];//每行的草地状态
    lol dp[maxn+5][400];//dp[i][j]表示第i行采用第j种状态可以有多少种方式
    
    int main()
    {
        for(int i=0,j=1;i<=(1<<maxn);i++)//首先满足自身相邻不都为1
        {
            if((i&(i<<1))==0)
                sat[j++]=i;
        }
        //for(int i=1;sat[i]<(1<<maxn);i++)  printf("%d
    ",sat[i]);
    
        int m,n;
        while(scanf("%d%d",&m,&n)!=EOF)
        {
            for(int i=1,temp;i<=m;i++)
            {
                temp=0;
                for(int j=1,a;j<=n;j++)
                {
                    scanf("%d",&a);
                    //反转
                    if(a==1)
                        a=0;
                    else
                        a=1;
                    temp=((temp<<1)|a);
                }
                row[i]=temp;
            }
    
            memset(dp,0,sizeof(dp));
            //第一行
            for(int i=1;sat[i]<(1<<n);i++)
            {
                if((sat[i]&row[1])==0)
                    dp[1][i]=1;
            }
            //按行递推
            for(int i=2;i<=m;i++)
            {
                for(int j=1;sat[j]<(1<<n);j++)
                {
                    if((sat[j]&row[i])==0)//选择的状态与本行的草地不冲突
                    {
                        for(int k=1;sat[k]<(1<<n);k++)
                        {
                            if((sat[k]&sat[j])==0)//与上一行的状态不冲突
                                dp[i][j]+=dp[i-1][k];
                        }
                    }
                }
            }
    
            lol ans=0;
            for(int i=1;sat[i]<(1<<n);i++)
                ans=(ans+dp[m][i])%mod;//注意取模
            printf("%lld
    ",ans);
        }
        return 0;
    }
    View Code
  • 相关阅读:
    SQL SERVER 2008 如何查询含有某关键词的表
    Windows 8.1 & Windows 10 取消 Windows Update 自动更新硬件驱动
    删除Visual Studio Online 中团队项目
    fedora Server 21 安装 Opera 29.0.1795.47
    Fedora 手动删除系统中不再需要的包
    GitHub 操作流程示例
    搬家至个人独立博客virson.cn
    使用Eclipse PDT + Xampp搭建Php开发环境
    Office 2013 Excel 转换 Word
    Windows 8.1 Enterprise 下 安装 Eclipse 官方中文包后无法输入任何内容
  • 原文地址:https://www.cnblogs.com/acboyty/p/9786795.html
Copyright © 2020-2023  润新知