poj2411 Mondriaan's Dream

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

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

这里写图片描述

数据范围:
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 <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#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<<m);i++){
        for(int j=0;j<(1<<m);j++){
            bool flag = true;
            for(int k=0;k<m;k++)if(flag){
                if((i&(1<<k)) != 0){  // i是1
                    if((j&(1<<k)) == 0){
                        continue;
                    }  // j和i都是1
                    k++; //比较后面一位
                    if( (k<m) && (i&(1<<k)) != 0 && (j&(1<<k)) != 0){
                        continue;
                    }else{
                        flag = false;
                        break;
                    }
                }else{
                    if((j&(1<<k)) != 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<<m)-1] = 1;
        for(int i=1;i<=n;i++){
            for(int j=0;j<total;j++){
                dp[i][path[j][1]] += dp[i-1][path[j][0]];
            }
        }
        printf("%lld\n",dp[n][(1<<m)-1]);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值