分析:
一个棋盘状压dp问题,由于放置一个国王,国王不能攻击周围8个格子,那么一共有以下三种情况
- 考虑上下 同一列相邻的位置不能放置国王 即(x>>i&1)&&(x>>(i+1)&1) 不能同时成立
- 考虑左右 同一行相邻的位置不能放置国王 即相邻两列状态 (a&b)==0
- 考虑四个顶点 也就是相邻两列的并集(a|b) 必须满足情况1
维护的状态:当前列号,当前列的状态,已经放置的数目
状态计算/子集划分:所有同时满足上面三个条件且k>=cnt[a]的集合
初始化:0
边界:dp[0][0][0]=1
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
using namespace std;
const int N=12,M=1<<N;
typedef long long ll;
ll dp[N][M][N*N];
vector<int> state;
vector<int> head[M];
bool st[M];
int cnt[M];
int n,m;
int count(int x)
{
int res=0;
for(int i=0;i<n;i++)
res+=x>>i&1;
return res;
}
bool check(int x)
{
for(int i=0;i<n;i++)
if((x>>i&1)&&(x>>(i+1)&1))
return false;
return true;
}
int main()
{
cin>>n>>m;
for(int i=0;i<1<<n;i++)
if(check(i))
state.push_back(i),st[i]=true,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 && st[a|b])
head[a].push_back(b);
}
dp[0][0][0]=1;
for(int i=1;i<=n+1;i++)
for(int j=0;j<state.size();j++)
for(int k=0;k<=m;k++)
{
int a=state[j];
if(k>=cnt[a])
for(auto b:head[a])
dp[i][a][k]+=dp[i-1][b][k-cnt[a]];
}
cout<<dp[n+1][0][m];
}