题面的链接
有点像八皇后,嗯,但是并不一样。
把每一行可以放的国王的情况压缩成二进制数,有国王用1表示,没有国王用0表示。
①预处理在一行之内合法的情况。也就是不能有相邻的1。
听说正解是用dfs跑的,我直接打了一个for循环。n最大只有9,状态数最多有2^9,不会超时。每种状态存在exist数组里
②预处理哪两种状态是可以挨着的,也就是上下两行可以同时存在的状态。同样记在一个数组里。上一个预处理时,可以把状态存到数组里,然后直接调用下标。存can数组
③同时,还要记录一下每一种状态的国王的数量,记为cnt数组。
④dp数组:dp[i][j][pos]表示放到第i行,已经放了j个国王,这一行的状态的下标为pos时的方案数。转移时,枚举上一行的状态,累加。即:dp[i][j][pos]=Σdp[i-1][j-cnt[pos]][k],其中,k为枚举的上一层的状态的下标
⑤注意数据范围要开long long
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
ll N,K,tot=1,max_exist,ans;
ll dp[15][100][1030],exist[1030],can[100][100],cnt[100];
void pre_exist()
{
exist[1]=0;
for(ll i=1;i<=max_exist;++i)
{
ll x=i,last=0,flg=0,num=0;
while(x)
{
if(x&1)
{
if(last==1)
{
flg=1;
break;
}
last=1;
num++;
}
else last=0;
x>>=1;
}
if(!flg)
{
exist[++tot]=i;
cnt[tot]=num;
}
}
//for(ll i=1;i<=tot;++i) printf("*%lld,%lld\n",i,exist[i]);
}
void pre_com()
{
for(ll i=1;i<=tot;++i) can[1][i]=can[i][1]=true;
for(ll i=2;i<=tot;++i)
for(ll j=i+1;j<=tot;++j)
if((exist[i]&exist[j])==0&&(exist[i]&(exist[j]<<1))==0&&(exist[i]&(exist[j]>>1))==0)
can[i][j]=can[j][i]=true;
}
void done()
{
for(ll i=1;i<=tot;++i) dp[1][cnt[i]][i]=1;
for(ll i=2;i<=N;++i)
for(ll j=0;j<=K;++j)
for(ll pos=1;pos<=tot;++pos)
{
if(cnt[pos]>K) continue;
for(ll k=1;k<=tot;++k)
if(can[pos][k]&&cnt[k]+cnt[pos]<=j)//不要忘记判断放的王的数量是否超过当前枚举的j
dp[i][j][pos]+=dp[i-1][j-cnt[pos]][k];
}
}
int main()
{
scanf("%lld%lld",&N,&K);
max_exist=(1<<N)-1;
pre_exist();
pre_com();
done();
for(ll i=1;i<=tot;++i)
ans+=dp[N][K][i];
printf("%lld",ans);
return 0;
}