poj2411 Mondriaan's Dream

poj2411 Mondriaan’s Dream
题 意:有一个n*m的矩阵,用1 乘 2的小矩阵去填充,完全填充,问有多少种方案。

这里写图片描述
如图展示了2乘2的矩阵的填冲的方法

poj2411 Mondriaan's Dream_第1张图片

数据范围:
1<=w<=11
1<=h<=11

思 路:状压dp思路,数值一般不超过16。如果小矩阵横着放,则用两个连续的1,1来表示。如果竖着放则上面用0,下面用1来表示。这样小矩阵的填充方式就唯一对应一种填充的方式。下面来考虑递推关系。
那么怎么选出第一行可行的状态呢?
这里由一个小技巧,设第0行全是1。然后确定第一行和后面的可行状态就都可以用同
如果第i-1是1 那i行则可以是0或者1,如果是i那么连续的偶数个都得是1.如果i-1是0那么第i行则必须是1
dp[i][j] 第i行对应装填为j的最大方法数。
path[i][0/1] i表示总共由多少总对应方案,path[i][0]表示上一行的状态,path[i][1]表示上下一行对应的状态.

dp[0 ][ (1 << m)-1] =1
dp[i][path[case][1]] += dp[i-1][path[case][0]]

收 获:明白了加行,使得状态转移更好确定。,加深的对状压dp的了解

#include 
#include 
#include 
#include 
#define N 1000005
using namespace std;
typedef long long ll;
const int maxn = 1e6+5;
ll dp[12][(1<<12)+1];  //dp[i][j]  前i行 状态为j对应的方案数
int path[maxn][2];
int n,m,total;
void init(){
    memset(dp,0,sizeof(dp));
    memset(path,0,sizeof(path));
    total = 0;
}
void get(){ //获取兼容模式
    for(int i=0;i<(1<for(int j=0;j<(1<bool flag = true;
            for(int k=0;kif(flag){
                if((i&(1<0){  // i是1
                    if((j&(1<0){
                        continue;
                    }  // j和i都是1
                    k++; //比较后面一位
                    if( (k1<0 && (j&(1<0){
                        continue;
                    }else{
                        flag = false;
                        break;
                    }
                }else{
                    if((j&(1<0) continue;  //必须要等于1
                    else{
                        flag = false;
                        break;
                    }
                }
            }
            if(flag){
                path[total][0] = i;
                path[total++][1] = j;
            }
        }
    }
}
int main() {
    while(~scanf("%d %d",&n,&m) && (n+m)){
        init();
        get();
        dp[0][(1<1] = 1;
        for(int i=1;i<=n;i++){
            for(int j=0;j1]] += dp[i-1][path[j][0]];
            }
        }
        printf("%lld\n",dp[n][(1<1]);
    }
    return 0;
}

你可能感兴趣的:(poj2411 Mondriaan's Dream)