0
点赞
收藏
分享

微信扫一扫

蒙德里安的梦想

泠之屋 2022-05-02 阅读 132
算法

题意:

将n*m的棋盘分割成若干个1*2的长方形,求方案数。

思路:

考虑按列摆放,某列的各行用0或1表示摆放状态。

若某行是1,表示横放,并且向前一列伸出。

若某行是0,表示竖放,或者由前一列伸出。

状态表示:f[i][j]表示摆放第i列,状态为j时的方案数。

状态转移:f[i][j]=\sum f[i-1][k] ,k是和j状态兼容的状态。

初始状态:f[0][0]=1,第0列不摆放是一种合法的状态

目标状态:f[m][0],第m列没有向下一列伸出的。

预处理:判断合并列的状态i是否合法(合并列就是前一列与当前列按位或)

若合并列的某行是1表示横放,0表示竖放。

若合并列不存在连续的奇数个0,即为合法状态.

代码:

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int N=12;
const int M=1<<N;
bool st[M];
int n,m;
ll f[N][M];

void solve(){
	for(int i=0;i<(1<<n);i++){
		st[i]=true;
		int cnt=0;//记录合并列中连续0的个数;
		for(int j=0;j<n;j++){
			if((i>>j)&1){
				if(cnt%2){
					st[i]=false;
					break;
				}
				cnt=0;
			}
			else
				cnt++;
		} 
		if(cnt%2)
			st[i]=false;
	}
	memset(f,0,sizeof(f));
	f[0][0]=1;
	for(int i=1;i<=m;i++){
		for(int j=0;j<(1<<n);j++){//状态:枚举第i列的状态 
			for(int k=0;k<(1<<n);k++){//状态:枚举第i-1列的状态 
			//两列状态兼容,不出现重叠的1,不出现连续奇数个0 
				if((j&k)==0&&st[j|k]){
					f[i][j]+=f[i-1][k];
				}
			}
		}
	}
	printf("%lld\n",f[m][0]);
}
int main(){
	while(1){
		scanf("%d%d",&n,&m);
		if(n==0&&m==0)
			break;
		solve();
	}	
}
举报

相关推荐

0 条评论