状态压缩DP 入门 POJ 3254

来源:互联网 发布:贵金属用什么软件 编辑:程序博客网 时间:2024/05/20 17:59

POJ 3254
  题目大意:给出一个M*N的矩阵,元素为0表示这个地方不能种玉米,为1表示这个地方能种玉米,现在规定所种的玉米不能相邻,即每行或者没列不能有相邻的玉米,问一共有多少种种植方法。结果
mod  100,000,000   (M,N<=12)
样例:
2 3
1 1 1
0 1 0

表示2*3的玉米地,现在一共有多少种种植方法呢? 答案:种0个玉米(算一个合法方案)+种1个玉米(4)+种2个玉米(3)+种3个玉米(1)=9
输出 9 


解题思路 :以样例数据第一行为例,三个格子都可以放牧,即每个格子都可以选择放,或不放。再考虑附加条件“相邻格子不可同时放牧”,那么我们可以列出单看第一行时的所有可行状态如下(1代表放牧,0代表不放牧)
编号 状态
1 1 1 1                       
2     0 1 0                       
由此,可将表中的状态看作二进制表示,那么,只需将每种状态转化为相应的十进制数,即可只用一个数字,就能表示某一种状态,如下表:
编号 二进制 十进制
1 1 1 1         7
2     0 1 0         2
这种用一个数来表示一组数,以降低表示状态所需的维数的解题手段,就叫做状态压缩。



根据题意,把每一行的状态用二进制的数表示,0代表不在这块放牛,1表示在这一块放牛。
首先很容易看到,每一行的状态要符合牧场的硬件条件,即牛必须放在能放牧的方格上。这样就能排除一些状态。
另外,牛与牛之间不能相邻,这样就要求每一行中不能存在两个相邻的1,这样也能排除很多状态。
然后就是根据上一行的状态转移到当前行的状态的问题了。必须符合不能有两个1在同一列(两只牛也不能竖着相邻)的条件。这样也能去掉一些状态。
然后,上一行的所有符合条件的状态的总的方案数就是当前行该状态的方案数。
状态压缩经常用到位运算(如以上几点全部靠位运算来实现)



这样,上一行的所有符合条件的状态的总的方案数就是当前行该状态的方案数

dp[i][j]=∑(dp[i-1][k])



#include<cstdio>#include<cstring>#define mod 100000000int n,m,a[13];int dp[13][(1<<13)];int check(int x,int y){   //x为第i行的初始矩阵的状态,y为第i行的状态y     if((a[x]|y)!=a[x])  //牛必须放在能放牧的方格上  也可以写成 a[x]&y!=y       return 0;    if(y&(y<<1))  //每一行中不能存在两个相邻的1       return 0;    return 1;}void solve(){    int i,j,k,max;dp[0][0]=1;max=1<<m;    for(i=1;i<=n;++i)//枚举每一行        for(j=0;j<max;++j){//每一行的状态            if(check(i,j)==0) continue;  //同一行的状态是否合法             for(k=0;k<max;++k){  //枚举这一行上面的所有状态 找出符合同一列上的合法状态                 if(j&k) continue;   //同一列上的状态是否合法                dp[i][j]=(dp[i][j]+dp[i-1][k])%mod;            }        }    int ans=0;    for(i=0;i<max;++i)        ans=(ans+dp[n][i])%mod;    printf("%d\n",ans);}int main(){    int x;    while(~scanf("%d%d",&n,&m)){        int i,j;        for(i=1;i<=n;++i){            a[i]=0;            for(j=1;j<=m;++j){                scanf("%d",&x);                a[i]=(a[i]<<1)+x;//初始矩阵每一行的状态             }        }        memset(dp,0,sizeof(dp));        solve();    }    return 0;}



0 0