题解:
我们可以假设dp转移方程为,dp[i[[j][k]
,这个转移方程的含义代表,第 i 行 第 j 个状态,k 个国王数量的方案数,一直推到第n行放置 所有国王的所有状态的方案数,累加即可。
因为互不侵犯,所以我们可以先得到行中满足状态的01串,也就是不能有相邻为1的情况,利用位运算很好解决 ((i>>1)&i)
这样如果有相邻的,那么答案就不会为0,没有相邻的,0和1的位置都错开,自然不会有问题,所以可以处理得到行没问题的。
void init()
{
for(ll i=0;i<(1<<n);i++)
{
if(!((i>>1)&i))
sta[++cnt]=i,work[cnt]=slov(i);
}
}
当然我们也要处理每一行,可行状态中国王的数量,也就是处理01串中1的数量。
ll slov(ll i)
{
ll res=0;
while(i)
{
res+=i&1;
i>>=1;
}
return res;
}
现在我们需要处理列,因为国王周围八个位置不能放东西,所以,再可行情况中,我们可以利用位运算去解决,总共有三种情况,国王位置出在同一竖直,左边,右边位置,三种情况,利用位运算:
if(sta[j]&sta[k])
continue;
if(((sta[j]>>1)&sta[k]))
continue;
if(((sta[j]<<1)&sta[k]))
continue;
以上就是状态dp的所有限制条件,我们可以由之前的状态得到状态转移方程:
dp[i][j][k]=dp[i-1][o][s-work[j]];
AC代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn=1e3+5;
ll dp[10][515][83];
ll work[515],n,cnt,sta[515];
ll slov(ll i)
{
ll res=0;
while(i)
{
res+=i&1;
i>>=1;
}
return res;
}
void init()
{
for(ll i=0;i<(1<<n);i++)
{
if(!((i>>1)&i))
sta[++cnt]=i,work[cnt]=slov(i);
}
}
int main()
{
ios::sync_with_stdio(false);
ll s;
cin>>n>>s;
init();
for(ll i=1;i<=cnt;i++)
dp[1][i][work[i]]=1;
for(ll i=2;i<=n;i++)
{
for(ll j=1;j<=cnt;j++)
{
for(ll k=1;k<=cnt;k++)
{
if(sta[j]&sta[k])
continue;
if(((sta[j]>>1)&sta[k]))
continue;
if(((sta[j]<<1)&sta[k]))
continue;
for(ll p=s;p>=work[j];p--)
{
dp[i][j][p]+=dp[i-1][k][p-work[j]];
}
}
}
}
ll res=0;
for(ll i=1;i<=cnt;i++)
{
res+=dp[n][i][s];
}
cout<<res<<endl;
}