蒙德里安的梦想(状压dp模板题)(个人详细理解)

3 篇文章 0 订阅
2 篇文章 0 订阅

求把 N×M 的棋盘分割成若干个 1×2 的的长方形,有多少种方案。

例如当 N=2,M=4 时,共有 5 种方案。当 N=2,M=3 时,共有 3 种方案。

如下图所示:

在这里插入图片描述

输入格式
输入包含多组测试用例。

每组测试用例占一行,包含两个整数 N 和 M。

当输入用例 N=0,M=0 时,表示输入终止,且该用例无需处理。

输出格式
每个测试用例输出一个结果,每个结果占一行。

数据范围
1≤N,M≤11

输入样例:
1 2
1 3
1 4
2 2
2 3
2 4
2 11
4 11
0 0
输出样例:
1
0
1
2
3
5
144
51205

(注:只考虑横向摆放状态,计算出所有横向的长方形摆放种类,所有纵向长方形就只能填满剩余空隙)

#include <iostream>
#include <cstring>
#include <vector>

using namespace std;

const int N=12,M=1<<N;	//因最终状态转移要用到第 m+1列,故 N至少要 12
						//M表示某一列的最大排列方案数 而题目中最多有11行,则某列的排列方案最多有 2<<11种 (状态表示为 0~2^11-1)
int n,m;
long long f[N][M];      //f的第一位表示当前列数,第二位表示上一列的状态 (例 f[n][5]表示当状态转移到第 n列时,第 n-1列摆放为 1001的情况下,总共的方案数

vector <int> state[M];  //state用来存储可与当前状态共存的上一列状态,用于状态转移 
						  //(例:state[3]存有所有可与当前这一列的状态(101)共存的上一列的状态

bool st[M];          //st用来表示单独一列状态是否合法(即不存在奇数个连续空行)     

int main()
{
    while(cin>>n>>m,n||m){
        for(int i=0;i<1<<n;i++){   //遍历单独一列的所有摆放状态
            st[i]=1;		       //假设合法
            int cnt=0;			   //用来记录连续空行个数
            for(int j=0;j<n;j++){  //遍历当前状态的每一个位置
                if(i>>j&1){ 	   //如果当前位置不为空
                    if(cnt&1){     //如果存在奇数个连续空行
                        st[i]=0;
                        break;     //直接break 无需后续判断(优化)
                    }
                    //cnt=0;(可有可无)
                }
                else cnt++;     //如果当前位置为空
            }
            if(cnt&1) st[i]=0;  //此处判断从最后一个长方形到边界的空行个数是否为奇数个
        }
        
        for(int i=0;i<1<<n;i++){	    //表示当前这一列
            state[i].clear();	        //因为有多组数据
            for(int j=0;j<1<<n;j++)	    //表示上一列
                if((i&j)==0&&st[i|j])	//当当前这一列与上一列的状态不处于同一行(不重叠),且空出的连续位置为偶数个 则为合法数据
                    state[i].push_back(j);	//将j(上一列)的状态存入state[i]中
        }
        memset(f,0,sizeof f);   //因为有多组数据
        f[0][0]=1;		        //开始时没有上一列,也可理解为上一列状态为 0,    所以在第一列时,上一列状态为 0的情况有 1种
        for(int i=1;i<=m;i++)	//表示处于第几列(0算第一列)
            for(int j=0;j<1<<n;j++)  //表示当前列状态
                for(auto k:state[j]) //遍历数组中所有元素(遍历能与当前列状态共存的上一列状态)
                    f[i][j]+=f[i-1][k];
        cout<<f[m][0]<<endl;         //f[m][0]表示处于m+1列时,第m列状态为0(即什么都不放)的情况下,总共的方案数
    }
    return 0;
}

(个人理解,如有错误欢迎大佬指正)

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值