蓄水池

时间:2019-11-10
本文章向大家介绍蓄水池,主要包括蓄水池使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

LuoguP1763

这道题想了很久。。。但仍然有些细节不太懂,所以打算等以后自己变强之后再来瞅瞅,怕自己忘,先来篇博客好了。。

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", &n, &m);
104     for (int i = 0; i < n; i++) {
105         scanf("%s", mp[i]);
106     }
107     printf("%d\n", solve());
108     return 0;
109 }
View Code

原文地址:https://www.cnblogs.com/Sundial/p/11830542.html