AcWing 291. 蒙德里安的梦想 Java
原题链接
中等
希望未来自己看这道题解时,能够发出当时自己真菜的感叹!
/*
横着摆放方式确定之后,整个摆放就确定了
如何判断当前方案是否合法:所有剩余位置,能否填充竖着的小方框,按列来看,每一列内部所有空着的小方块需要是偶数个
状态表示:f[i][j] 表示以将前i - 1列摆好,且从第i - 1列,伸出到第i列的状态是j的所有方案
这题的状态需要看三列进行理解
*/
import java.io.*;
class Main {
static int N = 12, M = 1 << N;
static long[][] f = new long[N][M];
static boolean[] st = new boolean[M]; // 当前列的所有空着的空格是否为偶数
public static void main(String[] args) throws IOException {
BufferedReader cin = new BufferedReader(new InputStreamReader(System.in));
while (true) {
String[] s = cin.readLine().split(" ");
int n = Integer.parseInt(s[0]);
int m = Integer.parseInt(s[1]);
if(n == 0 && m == 0) break;
// 枚举每一列的占位状态哪些是合法的
for (int i = 0; i < 1 << n; i++) {
int cnt = 0; // cnt 存储当前列连续0的个数
st[i] = true;
for (int j = 0; j < n; j++) {
if ((i >> j & 1) == 1) { // 当前位置是1
if ((cnt & 1) == 1) st[i] = false; // 奇数个,第i个位置不合法
cnt = 0;
} else {
cnt++;
}
}
if ((cnt & 1) == 1) st[i] = false; // 判断最后一列
}
// 对于每个新的输入要重新计算f
for (int i = 0; i < N; i++) {
for (int j = 0; j < M; j++) {
f[i][j] = 0;
}
}
f[0][0] = 1; // 空,只有一种方案
for (int i = 1; i <= m; i++) {
for (int j = 0; j < 1 << n; j++) {
for (int k = 0; k < 1 << n; k++) { // 枚举每个子集
if ((j & k) == 0 && st[j | k]) { // 所有合法的状态, (j & k) == 0 前一行伸过来的不能和当前行起冲突,另一个 st[j | k]决定偶数
f[i][j] += f[i - 1][k];
}
}
}
}
System.out.println(f[m][0]);
}
}
}