链接:登录—专业IT笔试面试备考平台_牛客网
来源:牛客网
题目描述
在N×N的棋盘里面放K个国王,使他们互不攻击,共有多少种摆放方案。
国王能攻击到它上下左右,以及左上 左下右上右下八个方向上附近的各一个格子,共8个格子。
输入描述:
只有一行,包含两个数N,K ( 1 ≤ N ≤ 9, 0 ≤ K ≤ N * N)
输出描述:
方案数。
示例1
输入
3 2
输出
16
分析:假设一个一个放置国王时,当前位置的放置与 上一行 和 当前行位置前 的状态有关,则可以直接用行的状态来转移。假设当前行状态为i,前一行状态为j,合法转移满足的条件有:
i & (i<<1)== 0;(不能相邻放置,即不能有连续的1)
i & j==0;(竖直方向不能同时放置)
i &(j>>1)== 0;i & (j<<1) == 0;(左上与右上不能同时放置)
转移方程为,f【i】【p】【j】+=f【i-1】【p-num【j】】【k】
注:f【i】【j】【k】表示第i行一共放置j个当前行状态为k,num数组为每种状态放置的数量(1的数量)
对于行合法的状态(无相邻1)和num数组可以提前打表求出。
参考代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define pb push_back
ll f[10][90][600];
int num[600];
int cal(int x)
{
int cnt=0;
while(x)
{
if(x&1)cnt++;
x>>=1;
}
return cnt;
}
int main ()
{
int n,K;cin>>n>>K;
vector<int>q;//存行的合法状态
for(int i=0;i<(1<<n);i++)if(!(i&(i<<1)))q.pb(i);
for(int i=0;i<(1<<n);i++)num[i]=cal(i);
f[0][0][0]=1;
for(int i=1;i<=n;i++)
{
for(int j=0;j<q.size();j++)
{
for(int k=0;k<q.size();k++)
{
if(q[j]&q[k])continue;
if(q[j]&(q[k]<<1))continue;
if(q[j]&(q[k]>>1))continue;
for(int p=0;p<=K;p++)
{
if(num[q[j]]>p)continue;
f[i][p][j]+=f[i-1][p-num[q[j]]][k];
}
}
}
}
ll ans=0;
for(int i=0;i<q.size();i++)ans+=f[n][K][i];
cout<<ans;
return 0;
}