比较容易理解的解法是直接枚举两行的状态。时间复杂度略高O(n*4^m)。
思路参考了该大牛的:请戳
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
#define maxn 11
typedef __int64 LL;
LL dp[12][1<<11];
int n,m;
bool judge(int s)
{
int cnt=0;
while(s)
{
if(s&1) ++cnt;
else {
if(cnt&1) return 0;
cnt=0;
}
s>>=1;
}
if(cnt&1) return 0;
return 1;
}
bool check(int s1,int s2)
{
for(int i=0;i<m;)
{
if(s1&(1<<i))
{
if(s2&(1<<i)){
if(i+1<m&&(s1&(1<<(i+1)))&&(s2&(1<<(i+1)))) i+=2;
else return 0;
}
else ++i;
}
else{
if(s2&(1<<i)) ++i;
else return 0;
}
}
return 1;
}
int main()
{
int i,j,k;
while(~scanf("%d%d",&n,&m)&&(n+m))
{
if(n<m) swap(n,m);
if(n*m%2) {printf("0\n");continue;}
memset(dp,0,sizeof(dp));
for(i=0;i<(1<<m);++i)
if(judge(i)) dp[1][i]=1;
for(i=2;i<=n;++i)
for(j=0;j<(1<<m);++j)
for(k=0;k<(1<<m);++k)
if(check(k,j)) dp[i][j]+=dp[i-1][k];
printf("%I64d\n",dp[n][(1<<m)-1]);
}
return 0;
}
另外一种方法是事先通过DFS预处理出两行之间的所有合法的状态转移:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<set>
#include<queue>
#include<stack>
#include<map>
using namespace std;
typedef __int64 LL;
#define maxn 14000
LL dp[12][1<<11];
int n,m,p1[maxn],p2[maxn],cnt;
void dfs(int now,int pre,int dep)
{
if(dep>m) return;
if(dep==m){
p1[cnt]=pre;
p2[cnt++]=now;
return;
}
dfs(now<<1,pre<<1|1,dep+1);
dfs(now<<1|1,pre<<1,dep+1);
dfs(now<<2|3,pre<<2|3,dep+2);
}
int main()
{
int i,j;
while(~scanf("%d%d",&n,&m)&&(n+m))
{
cnt=0;
if(m>n) swap(n,m);
dfs(0,0,0);
memset(dp,0,sizeof(dp));
dp[0][(1<<m)-1]=1;
for(i=1;i<=n;++i)
for(j=0;j<cnt;++j)
dp[i][p2[j]]+=dp[i-1][p1[j]];
printf("%I64d\n",dp[n][(1<<m)-1]);
}
return 0;
}
当然还可以用轮廓线DP来搞。
枚举每一个格子的轮廓线状态,总时间复杂度为O(n*m*2^min(n,m))
暂时还不会