这道题想了很久。。。但仍然有些细节不太懂,所以打算等以后自己变强之后再来瞅瞅,怕自己忘,先来篇博客好了。。
Code:
1 #include <bits/stdc++.h> 2 using namespace std; 3 const int mod = 12345678; 4 int n, m, tp, ans; 5 int x[8], y[8];//储存 X 的位置 6 int f[1 << 8][31];//如果 . 可以放蓄水池的话的方案数 i 为蓄水池的放数状态,j 为放到了第几个数 7 //整体思路是 设 . 可以放蓄水池,再使用容斥原理加或减新加的蓄水池的方案数 8 bool vis[4][7]; 9 char mp[4][7]; 10 bool in(int x, int y) { 11 return 0 <= x && x < n && 0 <= y && y < m; 12 } 13 int clac() {//用于求出把每个点作为蓄水池的方案数 14 tp = 0; 15 for (int i = 0; i < n; i++) { 16 for (int j = 0; j < m; j++) { 17 if (mp[i][j] == 'X') {//记录蓄水池的位置 18 x[tp] = i; 19 y[tp++] = j; 20 } 21 } 22 } 23 memset(f, 0, sizeof f); 24 f[0][0] = 1;//初始化 25 for (int s = 0; s < (1 << tp); s++) {//枚举状态 26 memset(vis, 1, sizeof vis); 27 for (int i = 0; i < tp; i++) {//枚举蓄水池个数 28 if (!(s & (1 << i))) {//当前状态的从右向左数第i+1位是否有蓄水池 29 for (int dx = -1; dx <= 1; dx++) { 30 for (int dy = -1; dy <= 1; dy++) {//枚举周围八个方向 31 if (in(x[i] + dx, y[i] + dy)) { 32 vis[x[i] + dx][y[i] + dy] = 0;//将蓄水池周围的格子标记 33 } 34 } 35 } 36 } 37 } 38 int cnt = 0; 39 for (int i = 0; i < n; i++) { 40 for (int j = 0; j < m; j++) { 41 if (vis[i][j]) { 42 cnt++;//周围有多少个格子没有被标记,即可能成为蓄水池的点 43 } 44 } 45 } 46 for (int i = 0; i <= cnt; i++) {//枚举新添加为蓄水池的个数 47 if (f[s][i]) {//有当前状态 48 f[s][i + 1] = (f[s][i + 1] + f[s][i] * (cnt - i)) % mod; 49 //乘(cnt-i)是因为i+1个蓄水池由i个蓄水池转移而来,那么第i+1个水池可以在剩下(cnt-i)的水池中随意选,乘法原理 50 for (int j = 0; j < tp; j++) {//枚举所有蓄水池 51 if (!(s & (1 << j))) {//当前状态的从右向左数第j+1位是否有蓄水池,如果没有就修上 52 f[s | (1 << j)][i + 1] = (f[s | (1 << j)][i + 1] + f[s][i]) % mod;//状态转移 53 //这里一定是用或运算,将两个状态所影响的并起来 54 } 55 } 56 } 57 } 58 } 59 return f[(1 << tp) - 1][n * m];//全放的方案数 60 } 61 void dfs(int x, int y, int k) {//x,y表示当前点,k用来判断该加上还是减去 62 if (x >= n) {//说明到了最后一行 63 ans = (ans + k * clac()) % mod; 64 } else if (y >= m) { 65 dfs(x + 1, 0, k);//到了最后一列,就换行 66 } else { 67 dfs(x, y + 1, k);//否则就下一列 68 bool flag = true; 69 for (int dx = -1; dx <= 1; dx++) {//枚举八个方向 70 for (int dy = -1; dy <= 1; dy++) { 71 if (in(x + dx, y + dy) && mp[x + dx][y + dy] == 'X') {//如果周围有蓄水池 72 flag = false;//说明该点不能被当做蓄水池 73 } 74 } 75 } 76 if (flag) { 77 mp[x][y] = 'X';//假设该点为蓄水池 78 dfs(x, y + 1, -k);//进行递归,并加或减去其方案数(容斥原理) 79 //奇数减去,偶数加上。因为如果1和2同时放错,中间重复的部分要加上 80 mp[x][y] = '.';//回溯改回来 81 } 82 } 83 } 84 int solve() { 85 for (int i = 0; i < n; i++) {//枚举行 86 for (int j = 0; j < m; j++) {//枚举列 87 if (mp[i][j] == 'X') {//如果当前是蓄水池 88 for (int dx = -1; dx <= 1; dx++) {//枚举八个方向 89 for (int dy = -1; dy <= 1; dy++) { 90 if ((dx || dy) && in(i + dx, j + dy) && mp[i + dx][j + dy] == 'X') {//如果其相邻的格子里也有蓄水池就按题目要求直接返回0 91 return 0; 92 } 93 } 94 } 95 } 96 } 97 } 98 ans = 0; 99 dfs(0, 0, 1);//递归求解 100 return (ans + mod) % mod; 101 } 102 int main () { 103 scanf("%d%d ", &n, &m); 104 for (int i = 0; i < n; i++) { 105 scanf("%s", mp[i]); 106 } 107 printf("%d ", solve()); 108 return 0; 109 }