题目链接:
https://www.acwing.com/problem/content/1066/
题解:
需要分析出来,第j+1列的摆放,只和第j列的摆放有关系,这很重要,本质上和蒙德里安那题是一致的,只是合法状态的转移计算更加复杂,需要预处理的地方更多。
AC代码:
// f[i][j][k]: 前i-1列已经摆好,且已经使用了j个国王,第i列为状态k的所有合法方案的数量
// 状态a -> 状态b为合法,a和b都不能有相邻的1,且(a & b) == 0 && (a | b)不能有相邻的1
import java.util.*;
public class Main {
static int N = 12, M = 110, K = 1 << 10;
static long[][][] f = new long[N][M][K];
static Map<Integer, List<Integer>> h = new HashMap<>();
static List<Integer> st = new ArrayList<>();
static int[] cnt = new int[K];
static int n, m;
// 判断st是否合法
static boolean check(int st) {
return (st & st >> 1) == 0;
}
static int count(int st) {
int res = 0;
for (int i = 0; i < n; i ++) {
if ((st >> i & 1) == 1) res ++;
}
return res;
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
m = sc.nextInt();
for (int i = 0; i < (1 << n); i ++) {
if (check(i)) {
st.add(i);
cnt[i] = count(i);
}
}
for (int i = 0; i < st.size(); i ++) {
for (int j = 0; j < st.size(); j ++) {
int a = st.get(i), b = st.get(j);
if ((a & b) == 0 && check(a | b)) {
List<Integer> list = h.getOrDefault(i, new ArrayList<>());
list.add(j);
h.put(i, list);
}
}
}
f[0][0][0] = 1;
for (int i = 1; i <= n + 1; i ++)
for (int j = 0; j <= m; j ++)
for (int u = 0; u < st.size(); u ++)
if (h.containsKey(u))
for (Integer v : h.get(u)) {
int a = st.get(u), b = st.get(v);
// 为什么不加上cnt[b] <= j - cnt[a]也是对的?
// 因为,状态集合只规定了算,合法方案,这种非合法方案数量的状态表示为0,加上也不会错
if (j - cnt[a] >= 0) {
f[i][j][u] += f[i - 1][j - cnt[a]][v];
}
}
System.out.println(f[n + 1][m][0]);
}
}