标签:状压dp
Description
Input
Output
Sample Input
1 2 1 3 1 4 2 2 2 3 2 4 2 11 4 11 0 0
Sample Output
1 0 1 2 3 5 14451205
这题可以用状压dp做,用二进制表示每一行的状态,横着的11表示横放,竖着的01表示竖放,然后先初始化第一行的可行状态,因为第一行前没有空行,所以转化后的二进制中如果有奇数个1连一起一定是不可行状态.但对于大于1的行来说,因为可能会有前面一行的矩形竖着放,所以奇数个1连在一起可能是可行的,所以需要另外的判断。可以发现,状态转移过程中,大于1的每一行都要满足两个条件,一个是行内不能有空余的位置(可以用|来实现,很神奇啊),另一个是如果去掉前一行竖着放的矩形遗留在当前行的1,当前状态一定也是可行状态(可以用&来实现,动手画一下),这样就可以把动态转移方程写出来了,我们记dp[i][state]为第i行state状态下的总方案数,那么dp[i][state]=dp[i][state]+dp[i-1][state‘],所以最后要求的就是dp[n][(1<<m)-1].
#include<stdio.h> #include<string.h> #define ll long long int kexing[5000],n,m; ll dp[15][5000]; int panduan(int x) { int i,j,tot=0; while(x>0){ if(x%2==1){ tot++;x=x/2; } else{ if(tot%2==1)return 0; tot=0;x=x/2; } } if(tot%2==1)return 0; else return 1; } int check(int x,int y) { int i,j,t=(1<<m)-1; if(!( (x|y)==t ) )return 0; return kexing[x&y]; } int main() { int i,j,k; while(scanf("%d%d",&n,&m)!=EOF) { if(n==0 && m==0)break; memset(dp,0,sizeof(dp)); for(i=0;i<(1<<m);i++){ if(panduan(i)){ kexing[i]=1;dp[1][i]=1; } else kexing[i]=0; } for(i=2;i<=n;i++){ for(j=0;j<(1<<m);j++){ for(k=0;k<(1<<m);k++){ if(check(j,k)){ dp[i][j]=dp[i][j]+dp[i-1][k]; } } } } printf("%lld\n",dp[n][(1<<m)-1]); } return 0; }
标签:状压dp
原文地址:http://blog.csdn.net/kirito_acmer/article/details/46618167