状压dp-POJ - 3254- 农夫玉米田

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

转大佬的思路:https://www.cnblogs.com/BlackStorm/p/4706243.html

对于0-1状态矩阵,自然而然会想到用状态压缩来做,把一行(也可以按列)的状态压缩成一个十进制数(行状态)。另种植or不种植也可以用0-1表示,并根据题目所说不能挨着种植,即这一行的某个位置种植了,下一行的同一位置就不能种植,可以知道两行的种植状态相位与要为0。

另外说一个行种植状态有效,即相邻的格子是不能种植的,需要左移一位后与自身相位与为0,如果存在相邻种植的格子,则一定会保留位1,不可能得出0的结果,据此枚举出这些状态再进行判断。

然而并不是每个有效的行种植状态对于每一行都有效,因为有的行存在一些位置是不能种植的,用0表示。为了方便判断和计算,我们考虑将行状态取反,即0表示肥沃,1表示不肥沃,这样只有当行种植状态和行状态相位与为0,这个种植状态才在该行有效,因为如果种在了不肥沃的格子上,相位与会保留位1,结果不为0。

于是我们有以下设计思路:
①枚举所有有效的种植状态,存到rec[]数组里,并将最大值存进去避免后面越界;
②在读入时就将格子状态取反,压缩成行状态存到row[]数组里;
③先处理第一行,给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 and rec[i]&rec[j]=0.
          
即rec[i]是row[i-1]的有效行状态,且rec[j]是row[r]的有效行状态,且rec[i]和rec[j]两个行状态不发生冲突。

很重要的结论:
1:预处理满足没有相邻放牧的所有的行时:需要左移一位后与自身相位与为0
2:所有满足的可能行于现在这个行没有冲突的条件:当行种植状态和行状态相位与为0
3:于上一行的状态没有冲突:两行的种植状态相位与要为0。

位运算很重要!位运算很重要!位运算很重要!还有不要把&写成&&。

#include 
using namespace std;
const int mod=100000000;

int xx[377];//所有合法的行状态
int now[13];//目前输入的行状态
int dp[13][377];

int main()
{

    int x=1<<12, k=0;
    for(int i=0;i<x;i++)
    {
        if(!(i&(i<<1)))      //行状态合法
        {
            xx[k++]=i;
        }
    }
    xx[k]=x;                //结束标志

    int n, m, t;
    scanf("%d%d",&n,&m);
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<m;j++)
        {
            scanf("%d",&t);
            now[i]=(now[i]<<1)|!t;//保存(取反)的行状态
        }
    }
    x=1<<m;//x目前的最大值

    for(int i=0;xx[i]<x;i++)//预处理第一行状态
    {
        if(!(xx[i]&now[0]))
        {
            dp[0][i]=1;
        }
    }

    for(int H=1;H<n;H++)
    {
        for(int i=0;xx[i]<x;i++)
        {
            if(!(xx[i]&now[H-1]))//枚举上一行可能的状态I
            {
                for(int j=0;xx[j]<x;j++)
                {
                    if(!(xx[j]&now[H]))//枚举这一行可能的状态J
                    {
                        if(!(xx[i]&xx[j]))//如果I和J不冲突
                        {
                            dp[H][j]=(dp[H][j]+dp[H-1][i])%mod;
                        }
                    }
                }
            }
        }
    }

    int ans=0;
    for(int i=0;xx[i]<x;i++)//最后一行的状态和为答案
    {
        ans=(ans+dp[n-1][i])%mod;
    }
    printf("%d\n", ans);

	return 0;
}

你可能感兴趣的:(状压dp)