这个题找到的题解很好,把轮廓线的原理解释得非常清楚
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;
}