这个题找到的题解很好,把轮廓线的原理解释得非常清楚

https://www.cnblogs.com/iiyiyi/p/5846864.html

其中,重点是这样一句话:

轮廓线状压的表示不是按照纵坐标大小从左到右,而是按照从左到右,从上至下的顺序(K4..K0)来的

也就是说,当我们填写图中O这个格子的时候,只有K4 - K0的状态会有影响,其他的一定都是1:否则不合法


那么,这个题的状态转移有三种:

A:(i,j)这个格子不放:条件是上面的放了

B:(i,j)这个格子和上面的一起放:条件是不在第一行,且上面的没放

C:(i,j)这个格子和左边的一起放:条件是不在第一列,且上面的放了,且左边的没放

怎么知道左边和上边放了没有呢?

这里就得用到轮廓线:把(K4K3K2K1K0)当作一个二进制数

那么K4是二进制数的第n-1位,判断上面就是k & (1<<(n-1))

K0是二进制数的第0位,判断左边就是k & 1


#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;

long long dp[2][1<<12];

int main(){
	int n,m,k;
	while(scanf("%d%d",&n,&m),n+m){
		if ((n*m)%2==1){
			puts("0");
			continue;
		}
		memset(dp,0,sizeof(dp));
		if (m>n) swap(n,m);
		int cur=0;
		dp[0][(1<<m)-1]=1;
		for(int i=1;i<=n;i++)
			for(int j=1;j<=m;j++){
				cur ^= 1;
				memset(dp[cur],0,sizeof(dp[cur]));
				for(int k=0;k<(1<<m);k++){
					if (k&(1<<(m-1))){
						//不放:上边已经放了
						//否则得到的是不合法的情况
						//上边的格子会一直是空着的
						int now=((k<<1)&((1<<m)-1));
						dp[cur][now]+=dp[cur^1][k];
					}
					if (i!=1&&!(k&(1<<(m-1)))){
						//放上边:不在第一行,上边没有放
						int now=((k<<1)|1)&((1<<m)-1);
						dp[cur][now]+=dp[cur^1][k];
					}
					if ((k&(1<<(m-1)))&&(j!=1)&&!(k&1)){
						//放左边:不在第一列,上边已经放了,且当前格子可以摆
						int now=((k<<1)|3)&((1<<m)-1);
						dp[cur][now]+=dp[cur^1][k];
					}
				}
			}
		printf("%lld\n",dp[cur][(1<<m)-1]);
	}
	return 0;
}