题目描述
在N×N的棋盘里面放K个国王,使他们互不攻击,共有多少种摆放方案。国王能攻击到它上下左右,以及左上左下右上右下八个方向上附近的各一个格子,共8个格子。
由于每摆上一个棋子,需要查看周围9个位置,导致搜索的复杂度直接爆炸((n^2)个格子里选k个格子满足条件,搜索复杂度不太好估计,但是(C(k,n^2))一定很大)
因此需要预处理每一行的合法状态,然后进行递推(状压dp),每一位所记录的状态都是合法的
对于每一行,先枚举这一行的预备状态,然后枚举这一行的预备容量(类似背包枚举容量),再枚举上一行的状态(这个状态满足递推关系所以满足题目条件),如果两行judge合法则从上一行的状态转移到这一行
设上一行的状态下标为k,这一行的状态下标为j(其所包含1的数量为num[j])
即写出状态转移方程:(dp[i][l][j] += dp[i - 1][l - num[j]][k])(类似背包,如果l-num[j]有状态会被加上来,没有就当无事发生);
别忘了初始状态为dp[0][0][0]=1,即第0行没有摆棋子的0号状态为合法状态
最后统计第n行有k个棋子的每个状态所满足条件的合法状态数即可
记得开long long
#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define ll long long
#define fastio {ios::sync_with_stdio(false);cin.tie(NULL);cout.tie(NULL);};
using namespace std;
const int inf = 1e9 + 7;
const int maxn = 1e4 + 10;
vector<pair<ll,ll> >zt;
ll dp[11][200][200];
ll judge(ll x)
{
ll cnt = 0;
int flag = 0;
while (x)
{
if (x & 1)
{
if (!flag)
flag = 1;
else
return 0;
cnt++;
}
else
flag = 0;
x >>= 1;
}
return cnt;
}
int main()
{
fastio;
ll n, K;
cin >> n >> K;
zt.push_back({0,0});
for (int i = 1; i < 1 << n; i++)
{
ll tot;
if (tot = judge(i))
{
//cout << i << " " << tot << endl;
zt.push_back({ i,tot });
}
}
dp[0][0][0] = 1;
for (int i = 1; i <= n; i++)
for (int j = 0; j < zt.size(); j++)
for (int l = zt[j].second; l <= K; l++)
for (int k = 0; k < zt.size(); k++)
{
int tmp = zt[j].first, ttmp = zt[k].first;
if (tmp & ttmp || (tmp << 1) & ttmp || (tmp >> 1)& ttmp)continue;
dp[i][l][j] += dp[i - 1][l - zt[j].second][k];
}
ll ans = 0;
for (int i = 0; i < zt.size(); i++)
ans += dp[n][K][i];
cout << ans;
return 0;
}