zoukankan      html  css  js  c++  java
  • 蓄水池

    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, &m);
    104     for (int i = 0; i < n; i++) {
    105         scanf("%s", mp[i]);
    106     }
    107     printf("%d
    ", solve());
    108     return 0;
    109 }
    View Code
  • 相关阅读:
    在java中有关于反射的皮毛----自己的简略认知
    在java中异常中的题目---重要的一点
    在一个陌生的环境里学习新的-----单例
    在java开发环境中,快捷键的使用及用法
    指针(一)
    #ifdef、#ifndef、#else、#endif执行条件编译
    oc中的数组
    控制循环结构
    oc中的枚举
    oc中类的实例化及方法调用
  • 原文地址:https://www.cnblogs.com/Sundial/p/11830542.html
Copyright © 2011-2022 走看看