题目传送门
题意: 在一个n*n的棋盘上放k个国王,让他们互不攻击,问你有多少种放置的方案。(一个国王会攻击与之相邻的8个格子里的国王。)(1 <=n <=9, 0 <= k <= n * n)
思路: 我们可以先预处理出单独一行时,合法的放置情况。然后我们用
f
[
i
]
[
j
]
[
q
]
f[i][j][q]
f[i][j][q]表示放完了前i行,第i行状态为j,总共放了q个的时候的方案数。i和q都好表示,但是我们需要考虑如何表示当前行状态j。考虑到一行最多9个,我们可以用二进制去表示,一个9位的二进制最大是512,其中除去不合法的情况,合法情况数量肯定是控制在
1
0
2
10^2
102级别的。然后我们考虑转移,先把第一行的所有合法情况置为1,然后从2开始枚举,当前行的状态j和上一行的状态p,再枚举当前总共放置q个国王。
f
[
i
]
[
j
]
[
q
]
+
=
∑
f
[
i
−
1
]
[
p
]
[
q
−
n
u
m
(
j
)
]
f[i][j][q] +=\sum f[i-1][p][q-num(j)]
f[i][j][q]+=∑f[i−1][p][q−num(j)],num(j)表示状态j中国王个数。
代码:
#include<bits/stdc++.h>
#define endl '\n'
#define mp make_pair
#define pb push_back
#define ll long long
#define int long long
#define pii pair<int,int>
#define sz(x) (int)(x).size()
#define all(x) (x).begin(),(x).end()
#define mem(a,b) memset(a,b,sizeof(a))
char *fs,*ft,buf[1<<20];
#define gc() (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<20,stdin),fs==ft))?0:*fs++;
inline int read()
{
int x=0,f=1;
char ch=gc();
while(ch<'0'||ch>'9')
{
if(ch=='-')
f=-1;
ch=gc();
}
while(ch>='0'&&ch<='9')
{
x=x*10+ch-'0';
ch=gc();
}
return x*f;
}
using namespace std;
const int N=2e5+10;
const int inf=0x3f3f3f3f;
const int mod=998244353;
const double eps=1e-7;
int n,k;
vector<int>v;
void dfs(int x, int num, int cur)
{
if (cur >= n) // 有新的合法状态
{
v.pb(x);
return ;
}
dfs(x, num, cur + 1); // cur位置不放国王
dfs(x + (1 << cur), num + 1,cur + 2); // cur位置放国王,与它相邻的位置不能再放国王
}
int get(int x)
{
int cnt=0;
while(x)
{
if(x&1)
cnt++;
x>>=1;
}
return cnt;
}
int f[10][600][100];
int solve()
{
cin>>n>>k;
dfs(0,0,0);
for(int i=0; i<v.size(); i++)
f[1][i][get(v[i])] = 1;
for(int i=2; i<=n; i++)
{
for(int x=0; x<v.size(); x++)
{
for(int y=0; y<v.size(); y++)
{
int j = v[x], p = v[y];
if(j&p)
continue;
if((j<<1)&p)
continue;
if((j>>1)&p)
continue;
for(int q=get(j); q<=k; q++)
{
f[i][x][q] += f[i-1][y][q-get(j)];
}
}
}
}
int res=0;
for(int i=0; i<v.size(); i++)
res += f[n][i][k];
cout<<res<<endl;
return 0;
}
signed main()
{
// int _;
// cin>>_;
// while(_--)
solve();
}