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