在 n×n的棋盘上放 k 个国王,国王可攻击相邻的 88 个格子,求使它们无法互相攻击的方案总数。
输入格式
共一行,包含两个整数 nn 和 kk。
输出格式
共一行,表示方案总数,若不能够放置则输出00。
数据范围
1≤n≤101≤n≤10,
0≤k≤n20≤k≤n2
输入样例:
3 2
输出样例:
16
思路:
状态压缩DP
f(i,j,m)表示第i行已经使用了j个国王,状态为m时的方案数 ;
上下两行的状态 a ,b 如果不冲突需要满足的关系为
- a、b本身时合法的(二进制没有连续的1出现)
- a|b 的结果是合法的
- a&&b==0
所以,预处理判断 [1,1<<n] 所有数字二进制的状态,储存合法的状态,并且预处理和储存所有相邻两行的合法状态;之后开始DP
- 转移状态方程 : f( i , j, a) += f( i-1, j-c, b) c 是当第i行为状态a时能放的国王数量
答案就是 max{ f( n, k, a) } 枚举所有合法状态a
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=12,M=1<<N,K=110;
vector<int> state;//储存合法状态
int cnt[M];
vector<int> h[M];//记录所有相邻得合法状态
ll f[N][K][M];
int n,k;
bool check(int x)//判断是否有相邻的1(两个国王挨着放)
{
if(x&(x<<1)) return false;
return true;
}
int count(int x)//判断该状态有几个国王
{
int res=0;
for(int i=0;i<n;i++)
{
res+=x>>i&1;
}
return res;
}
int main()
{
cin >>n>>k;
for(int i=0;i<1<<n;i++)
{
if(check(i)) state.push_back(i),cnt[i]=count(i);
}
for(int i=0;i<state.size();i++)
{
for(int j=0;j<state.size();j++)
{
int a=state[i],b=state[j];
if(((a&b)==0)&&check(a|b)) h[a].push_back(b);
}
}
f[0][0][0]=1;//初始化
for(int i=1;i<=n+1;i++)
{
for(int j=0;j<=k;j++)
{
for(int x=0;x<state.size();x++)
{
for(auto y : h[state[x]])
{
int c=cnt[state[x]];
if(j>=c)//当前行的的这个状态中国王的数量要小于此刻国王总数
{
f[i][j][state[x]]+=f[i-1][j-c][y];
}
}
}
}
}
cout <<f[n+1][k][0]<<endl;
}