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.
This file contains invisible Unicode characters that may be processed differently from what appears below. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to reveal hidden 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 ;
// 棋盘式状态压缩DP
typedef long long LL ;
const int N = 12 ; // 棋盘的长宽上限,建议多开两个,防止溢出
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 ] ; // 某个状态兼容哪些状态(预处理的结果),注意这个上限M, 2022年8月13日曾经卡在这里2小时, 被一个同学误导了
int cnt [ M ] ; // 记录每种状态中的数字1个数, 快速获取某行使用了多少个国王
LL f [ N ] [ K ] [ M ] ; // 完成前i行, 使用了j个国王, 现在的状态是k:001010111之类, 存在的是二进制对应的十进制数
// 判断数字x是不是有连续的1
bool check ( int x ) {
return ! ( x & x > > 1 ) ;
}
// 数字1的个数
int count ( int x ) {
int res = 0 ;
while ( x ) {
x = x & ( x - 1 ) ;
res + + ;
}
return res ;
}
int main ( ) {
scanf ( " %d %d " , & n , & m ) ;
// 1、筛选掉: 同行出现连续1,保证同行不能出现连续1, 表示国王不相邻
// 并且记录每个状态中数字1的个数是多少
for ( int i = 0 ; i < 1 < < n ; i + + ) // 从啥也不摆到火力全开
if ( check ( i ) ) {
st . push_back ( i ) ; // 记录合法状态i
cnt [ i ] = count ( i ) ; // 记录合法状态i中有多少个国王( 数字1)
}
// 双重循环遍历,找出相邻行之间的兼容关系
for ( int a : st )
for ( int b : st ) {
if ( ( a & b ) = = 0 & & check ( a | b ) ) // 上下行, 45度双重检查
head [ a ] . push_back ( b ) ; // 记录合法的状态转移关系
}
// 3、DP
// 已经摆完了前0行, 放置了0个国王, 当前状态全是0, 这种情况下只有全是0的状态是合法的, 方案数为1
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 ] ) { // a状态的所有合法前序状态
int c = cnt [ a ] ; // 状态a的国王数量
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 ] ;
cout < < ans < < endl ;
return 0 ;
}