状态压缩dp poj2411 1*2砖块

来源:互联网 发布:股票数据api https 编辑:程序博客网 时间:2024/06/05 10:40

一开始想着用00,  11表示同一个砖,但是这样的话状态转移就难了,,然后看题解。题解的状态就不会冲突。






  1. /*分析:用1*2的砖去恰好铺满n*m的空间,对于第k行第j列,有3种情况将该点铺满  
  2. 1:由第k-1行第j列砖竖着铺将第k行第j列铺满  
  3. 2:由第k行第j列被横铺砖铺满  
  4. 3:第k行第j列砖竖着铺将该点铺满  
  5. 所以对于每一列的情况其实有两种(1,0)表示该点铺砖还是不铺  
  6. 而对于每一列必须到达的状态只有一种,就是被铺满(1)  
  7. 但是由上述3种情况将铺满方式分成两种:  
  8. 0和1表示被k-1行j列竖铺铺满和在k-1行被横铺铺满   
  9. 对于每一行列举每一种到达的状态j,dp[j]表示到达该状态有多少种情况  
  10. 分析对于第k-1行状态j:10000111  
  11. 需要到达第k行状态i:  01111011  
  12. 如果需要到达第k行j列状态是0,则必须第k-1行该点状态不能是0,否则一定是连续两列竖放冲突  
  13. 所以到达第k-1行该点只能是1,也就是说i|j一定每一位是1,也可以一步步判断是否满足第k行j列是0第k-1行j列是1   
  14. 如果需要到达第k行状态j列是1,则假如第k-1行该点是0,则该点状态可以到达,继续判断j+1列  
  15. 假如第k-1行该点是1,则第k行j列的1一定是横铺到达的,所以k行第j+1列一定也被铺满为1  
  16. 从而第k-1行j+1列一定不能竖铺,必须被横铺铺满,所以也是1.  
  17. 于是综合的第k行j列和第k-1行j列的关系(每一行每一列都表示到达的状态)  
  18. 1:下面这种情况从第j列继续去判断j+1列   
  19.   1  
  20.   0  
  21. 2:下面这种情况从第j列继续去判断j+1列   
  22.   0  
  23.   1  
  24. 3:下面这种情况从第j列判断第j+1列是否全是1,然后继续判断第j+2列  
  25.   1  
  26.   1   
  1. #include <iostream>  
  2. #include <cstdio>  
  3. #include <cstdlib>  
  4. #include <cstring>  
  5. #include <string>  
  6. #include <queue>  
  7. #include <algorithm>  
  8. #include <map>  
  9. #include <cmath>  
  10. #include <iomanip>  
  11. #define INF 99999999  
  12. typedef long long LL;  
  13. using namespace std;  
  14.   
  15. const int MAX=(1<<11)+10;  
  16. int n,m;  
  17. LL temp[MAX],dp[MAX],bin[15];  
  18. bool mark[MAX];  
  19.   
  20. bool check(int i){  
  21.     while(i){  
  22.         if(i&1){  
  23.             i>>=1;  
  24.             if(!(i&1))return false;//第j列是1则第j+1列必须是1   
  25.             i>>=1;//继续判断下一列   
  26.         }else i>>=1;//继续判断下一列   
  27.     }  
  28.     return true;  
  29. }  
  30.   
  31. void Init(){  
  32.     memset(mark,false,sizeof mark);  
  33.     memset(temp,0,sizeof temp);  
  34.     for(int i=0;i<bin[m];++i){//初始化第一行和可以到达什么状态   
  35.         if(check(i))temp[i]=1,mark[i]=true;  
  36.     }  
  37. }  
  38.   
  39. void DP(){  
  40.     for(int k=2;k<=n;++k){  
  41.         for(int i=0;i<bin[m];++i)dp[i]=0;  
  42.         for(int i=0;i<bin[m];++i){  
  43.             for(int j=0;j<bin[m];++j){  
  44.                 if((i|j) != bin[m]-1)continue;//每一位或之后必须每一位是1(综合前面3种情况和分析可知)  //起码让他要全为1
  45.                 if(!mark[i&j])continue;//由初始化和前面分析三种情况分析可知i&j必须得到和初始化可以到达的状态一样才行  //然后要他不出现连续3个奇数的情况
  46.                 dp[i]+=temp[j];//i可以从j到达,则增加j的方案数   //
  47.             }  
  48.         }  
  49.         for(int i=0;i<bin[m];++i)temp[i]=dp[i];  //相当于滚动数组,是的temp为上一列的数组。
  50.     }  
  51. }  
  52.   
  53. int main(){  
  54.     bin[0]=1;  
  55.     for(int i=1;i<12;++i)bin[i]=2*bin[i-1];  
  56.     while(~scanf("%d%d",&n,&m),n+m){  
  57.         if(n<m)swap(n,m);//始终保持m<n,提高效率   
  58.         Init();  
  59.         DP();  
  60.         printf("%lld\n",temp[bin[m]-1]);//输出最后一行到达时的状态必须全部是1   
  61.     }  
  62.     return 0;  
  63. }  

0 0
原创粉丝点击