今天刚刚学状压DP先来了手入门哈哈哈,效率可能没有那么高。
dp的定义dp[i][t][k] 第 i 行状态为 t 且放置 KING 的数目为 k 的方案数。
遍历第i行的状态值t从0开始到((1 << N) - 1) 如果 (t << 1) & t不为0说明有king放置在了一起
然后从0 到 ((1 << N) - 1)遍历合法的上一行状态 如果不合法就continue掉 上一行与当行不合法状态为
(pj << 1) & pj) || ((pj & j) || ((pj << 1) & j) || ((pj >> 1) & j)
对整体取非 则如果这个条件成立就说明不合法continue掉
然后更新k值就可以啦
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<map>
#include<vector>
#include<utility>
#define pb push_back
using namespace std;
typedef long long ll;
// __builtin_popcount() 计算32位有多少个1
ll dp[15][(1 << 9) + 10][88]; // dp[i][t][k] 表示第i行状态位t放置了k个King的方案
void solve(int _) {
int N, K; scanf("%d%d", &N, &K);
memset(dp, 0, sizeof(dp));
dp[0][0][0] = 1; // 初始化
// 遍历 行数
for(int i = 1; i <= N; i++) {
// 遍历状态
for(int j = 0; j <= ((1 << N) - 1); j++) {
// 确保行合法
// (j << 1) & j如果不为 0 说明存在非法状态
if((j << 1) & j) {
continue;
}
// 遍历上一行的状态
for(int pj = 0; pj <= ((1 << N) - 1); pj++) {
// 保留可用的上一行状态 即合法状态且在该为选为king时本状态的上 上左 上右不能当选king
if(((pj << 1) & pj) || ((pj & j) || ((pj << 1) & j) || ((pj >> 1) & j)) ) {
continue;
}
// 遍历 k 这一行k的大小
for(int k = __builtin_popcount(j); k <= K; k++) {
dp[i][j][k] += dp[i - 1][pj][k - __builtin_popcount(j)];
}
}
}
}
ll ans = 0;
for(int i = 0; i <= (1 << N) - 1; i++) {
ans += dp[N][i][K];
}
printf("%lld", ans);
}
int main() {
int _ = 1;
// scanf("%d", &_);
int c = 1;
while(c <= _){
solve(c);
c++;
}
return 0;
}