You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

60 lines
2.6 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 11; //棋盘的长宽上限
const int M = 1 << 10; //二进制枚举的状态数量上限因为n最大是10,就是2^10个状态
const int K = 110; //国王的个数上限
int n; //n*n的棋盘
int m; //国王的数量
vector<int> st; //所有合法的状态(预处理的结果)
vector<int> head[M]; //某个状态兼容哪些状态(预处理的结果)
int cnt[M]; //记录每种状态中的数字1个数了解本行使用了多少个国王
//完成前i行使用了j个国王现在的状态是k:001010111之类存在的是二进制对应的十进制数
LL f[N][K][M];
//判断一行是不是有连续1
bool check(int x) {
return !(x & x >> 1);
}
//统计某个状态中数字1的数量
int count(int x) {
int res = 0;
for (int i = 0; i < 32; i++) res += x >> i & 1;
return res;
}
int main() {
cin >> n >> m;
//1、可行的合法状态预处理
for (int i = 0; i < 1 << n; i++)
if (check(i)) st.push_back(i), cnt[i] = count(i);
//i与i-1行之间的兼容关系记录下来
for (int a: st)
for (int b: st)
//a&b==0:同列不是同时为1表示列上面国王不冲突
//check(a|b) 经或处理后的数字如果存在连续的1就表示斜45度有国王不合法,妙不可言
if ((a & b) == 0 && check(a | b)) head[a].push_back(b);//记录合法的状态转移关系
//2、DP
//已经摆完了前0行放置了0个国王当前状态全是0这种情况下只有全是0的状态是合法的方案数为0.
f[0][0][0] = 1;
for (int i = 1; i <= n; i++) //枚举每一行
for (int j = 0; j <= m; j++) //枚举国王个数
for (int a: st) { //枚举第i行的每一种可能状态
for (int b: head[a]) { //s状态与哪些状态兼容
int c = cnt[a]; //状态st[s]的国王数量也可以一并预处理出来,当然也可以现用现算
//上面的j循环限定了国王的数量上限
if (j >= c) f[i][j][a] += f[i - 1][j - c][b];//从上一层的状态转化而来
}
}
//结果
LL ans = 0;
//在填充完n行之后将m个国王放完每一个合法状态都是可能的解需要累加起来才是答案
for (int a: st) ans += f[n][m][a];
printf("%lld", ans);
return 0;
}