#include <bits/stdc++.h>
using namespace std;
/*
题目大意:
要在n*n的棋盘里放k个国王,在保证国王之间不能互相攻击的条件下,有多少个方案。
国王能攻击上下左右,以及左上左下右上右下八个方向。
思路:
定义一个数组dp[][][],第一维表示当前行;第二维表示当前行国王摆放的状态,0表示
当前行的某列没有国王,1表示当前行的某列有国王;第三维表示前i行摆放的国王总数。
dp[i][j][k]表示第i行状态为j,前i行国王的个数为k时的方案数。
预处理一行能摆放的国王的状态,以及该状态需要国王的个数。从第二行开始dp,每一行
的状态只受前一行的状态,以及前面行已经放置的国王个数的影响。
*/
typedef long long ll;
typedef pair<int, int> pii;
const int INF = 0x3f3f3f3f;
const double eps = 1e-6;
const double pi = acos(-1.0);
int s[1 << 10];
ll num[1 << 10];
ll dp[10][1 << 10][100];
int main()
{
int n, tot;
scanf("%d%d", &n, &tot);
// 预处理
int cnt = 0;
for(int i = 0; i < (1 << n); i++)
{
if((i & (i << 1)) == 0)
{
s[++cnt] = i;
for(int j = 0; j < n; j++)
{
if((i >> j) & 1)
{
num[cnt]++;
}
}
}
}
for(int i = 1; i <= cnt; i++)
{
dp[1][s[i]][num[i]] = 1;
}
for(int i = 2; i <= n; i++)
{
for(int j = 1; j <= cnt; j++)
{
for(int k = 1; k <= cnt; k++)
{
if((s[j] & s[k]) == 0 && (s[j] & (s[k] << 1)) == 0 && (s[j] & (s[k] >> 1)) == 0)
{
for(int l = num[k]; l <= tot; l++)
{
dp[i][s[k]][l] += dp[i - 1][s[j]][l - num[k]];
}
}
}
}
}
ll ans = 0;
for(int i = 1; i <= cnt; i++)
{
ans += dp[n][s[i]][tot];
}
printf("%lld\n", ans);
return 0;
}
洛谷-P1896(状压dp)
于 2022-08-16 21:58:37 首次发布