题目链接
http://www.lydsy.com/JudgeOnline/problem.php?id=1087
思路
首先预处理出对于单独的一行而言的所有合法的状态,然后预处理出相邻两行合法的状态对 (S1,S2) 。然后直接DP就行了
代码
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#define MAXN 100
using namespace std;
typedef long long int LL;
LL f[MAXN][MAXN][600],ans; //f[i][j][S]=DP到第i行,第i行状态为S,已经用了j个棋子的方案数
int status[MAXN];
int map[MAXN][MAXN],cnt[MAXN]; //map[i][j]=true表示上一行状态为i,这一行状态为j是可行的,cnt[i]=第i种状态对应的棋子数
int n,k,tot=0; //tot=单独一行的可行状态总数
void predfs(int x,int pos,int now) //已经放了x个棋子,上一次在pos位置放了一个棋子
{
status[++tot]=now;
cnt[tot]=x;
if(x>=(n+1)/2||x>=k) return;
for(int i=pos+2;i<=n;i++)
predfs(x+1,i,now+(1<<(i-1)));
}
void prework() //预处理出map数组
{
for(int i=1;i<=tot;i++)
for(int j=1;j<=tot;j++)
{
if((status[i]&status[j])||((status[i]>>1)&status[j])||((status[i]<<1)&status[j]))
map[i][j]=0;
else
map[i][j]=1; //!!!!
}
for(int i=1;i<=tot;i++) //dp预处理
f[1][cnt[i]][i]=1; //!!!!
}
int main()
{
scanf("%d%d",&n,&k);
predfs(0,-1,0);
prework();
for(int i=2;i<=n;i++) //第i行
for(int j=0;j<=k;j++) //已经用了j个棋子
for(int nextS=1;nextS<=tot;nextS++) //第i行状态为nextS
{
if(cnt[nextS]>j) continue;
for(int S=1;S<=tot;S++) //枚举第i-1行状态nextS
if(map[S][nextS]&&cnt[S]+cnt[nextS]<=j)
f[i][j][nextS]+=f[i-1][j-cnt[nextS]][S];
}
LL ans=0;
for(int i=1;i<=tot;i++)
ans+=f[n][k][i];
printf("%lld\n",ans);
return 0;
}