在 n×n 的棋盘上放 k 个国王,国王可攻击相邻的 8 个格子,求使它们无法互相攻击的方案总数。
输入格式
共一行,包含两个整数 n 和 k。
输出格式
共一行,表示方案总数,若不能够放置则输出0。
数据范围
1≤n≤10,
0≤k≤n2
输入样例:
3 2
输出样例:
16
代码:
// 棋盘类状压DP
#include <bits/stdc++.h>
using namespace std;
const int N = 12, M = 1 << 10, K = 110;
long long f[N][K][M]; // f[i][j][k]代表前i行已经放好了j个国王且当前最后一行的状态下标为k的合法方案数
vector<int> st;
vector<vector<int>> head; // head[i]存放下标为i的状态所能转移到的状态下标, state则存放每个合法状态
int cnt[M];
int n, m;
bool check(int i) // 检查该下标对应的状态中是否没有相邻的1
{
if (i & i >> 1)
return false;
else
return true;
}
int count(int i)
{
int ans = 0;
for (int j = 0; j < n; j++)
if (i >> j & 1)
ans++;
return ans;
}
int main()
{
cin >> n >> m;
/* 预处理出合法的状态过程:*/
for (int i = 0; i < 1 << n; i++)
{
if (check(i))
{
st.push_back(i);
cnt[i] = count(i);
}
}
head.resize(st.size() + 1);
/* 预处理出合法的状态转移过程: */
for (int i = 0; i < st.size(); i++)
{
for (int j = 0; j < st.size(); j++)
{
int a = st[i], b = st[j];
if ((a & b) == 0 && check(a | b))//这里第一个检查上下攻击,第二个检查两斜对角攻击
head[i].push_back(j);
}
}
f[0][0][0] = 1;
for (int i = 1; i <= n + 1; i++)//n行转移,n+1行是为了好统计答案
{
for (int j = 0; j <= m; j++)
{
for (int a = 0; a < st.size(); a++)
{
for (auto b : head[a])
{
int c = cnt[st[a]];
if (j >= c)
f[i][j][a] += f[i - 1][j - c][b];
}
// 此刻第i行(即最后一行)状态(下标)为a, 已经放好j个国王的方案数
// 由 第i - 1行状态(下标)为b, 已经放好j - num个国王的方案数转移过来
// 即 f[i][j][a] 由 f[i - 1][j - num][b] 转移而来
}
}
}
cout << f[n + 1][m][0];
// 输出到n + 1行, 已经放了m个国王, 当前最后一行的状态下标为0的合法方案数
// 即相当于刚好把m个国王放进n * n的棋盘后的方案数
// 这样就不用像上面的把f[n][m][0 ~ state.size() - 1]都累加起来的操作了
return 0;
}