BZOJ #2669 / CQOI 2012 局部最小值 (DP与容斥的完美结合)

题目描述:

在一个$N*M$的矩阵中填入$1\sim N*M$的数,并限制一些位置为周围$9$个格中最小的,而其它位置不能满足这个条件。

解题思路:

考虑$dp$,$F_{i,s}$表示填了前$i$个数,限制位置的填数状态为$s$,$cnt_s$表示限制位置的状态为$s$时,可以填数的位置+限制位置已填数的数量。

那么$F_{i,s}=F_{i-1,s}*(cnt_s-i+1)+\sum_{p\in s}F_{i-1,s-p}$。

但这样会忽略一些非限制的点满足了限制条件的情况。那我们就$dfs$把一些非限制点当作限制点,用上面$dp$做,再容斥掉。

代码:

  1 #include <cstdio>
  2 #include <cstring>
  3 #include <algorithm>
  4 using namespace std;
  5 
  6 const int mo = 12345678, fx[8][2] = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}, {1, 1}, {1, -1}, {-1, 1}, {-1, -1}};
  7 int T, n, m, tot, cnt[1 << 8], f[30][1 << 8], S, mar[10][10], ans;
  8 char s[10][10];
  9 
 10 void init() {
 11     tot = 0;
 12     for (int i = 1; i <= n; i ++)
 13         for (int j = 1; j <= m; j ++) if (s[i][j] == 'X') {
 14             tot ++;
 15             mar[i][j] = tot;
 16         } else mar[i][j] = 0;
 17     S = (1 << tot) - 1;
 18     for (int s = 0; s <= S; s ++) {
 19         int t = 0;
 20         for (int i = 1; i <= n; i ++)
 21             for (int j = 1; j <= m; j ++) {
 22                 if (mar[i][j] && ((1 << mar[i][j] - 1) & s)) t ++;
 23                 if (!mar[i][j]) {
 24                     int p = 0;
 25                     for (int k = 0; k < 8; k ++) {
 26                         int ni = i + fx[k][0], nj = j + fx[k][1];
 27                         if (ni < 1 || ni > n || nj < 1 || nj > m) continue;
 28                         if (mar[ni][nj] && (!((1 << mar[ni][nj] - 1) & s))) {
 29                             p = 1;
 30                             break;
 31                         } 
 32                     }
 33                     if (!p) t ++;
 34                 }
 35             }
 36         cnt[s] = t;    
 37     }
 38 }
 39 
 40 int dp() {
 41     for (int i = 1; i <= n * m; i ++)
 42         for (int s = 0; s <= S; s ++) {
 43             (f[i][s] = f[i - 1][s] * (cnt[s] - i + 1)) %= mo;
 44             for (int p = 1; p <= tot; p ++) if ((1 << p - 1) & s) {
 45                 (f[i][s] += f[i - 1][s ^ (1 << p - 1)]) %= mo;
 46             }
 47         }
 48     return f[n * m][S];
 49 }
 50 
 51 void dfs(int x, int y, int sum) {
 52     if (y == m + 1) {
 53         dfs(x + 1, 1, sum);
 54         return;
 55     }
 56     if (x == n + 1) {
 57         init();
 58         (ans += dp() * (sum & 1 ? -1 : 1) + mo) %= mo;
 59         return;
 60     } 
 61     dfs(x, y + 1, sum);
 62     int p = 0;
 63     for (int k = 0; k < 8; k ++) {
 64         int nx = x + fx[k][0], ny = y + fx[k][1];
 65         if (nx < 1 || nx > n || ny < 1 || ny > m) continue;
 66         if (s[nx][ny] == 'X') {
 67             p = 1;
 68             break;
 69         }
 70     }
 71     if (s[x][y] == 'X') return;
 72     if (!p) {
 73         s[x][y] = 'X';
 74         dfs(x, y + 1, sum + 1);
 75         s[x][y] = '.';
 76     }
 77 }
 78 
 79 int cheak() {
 80     for (int i = 1; i <= n; i ++)
 81         for (int j = 1; j <= m; j ++) if (s[i][j] == 'X') 
 82             for (int k = 0; k < 8; k ++) {
 83                 int ni = i + fx[k][0], nj = j + fx[k][1];
 84                 if (ni < 1 || ni > n || nj < 1 || nj > m) continue;
 85                 if (s[ni][nj] == 'X') return 0;
 86             }
 87     return 1;
 88 }
 89 
 90 int main() {
 91     scanf("%d", &T);
 92     f[0][0] = 1;
 93     while (T --) {
 94         ans = 0;
 95         scanf("%d %d", &n, &m);
 96         for (int i = 1; i <= n; i ++) scanf("%s", s[i] + 1);
 97         if (!cheak()) {
 98             printf("0\n");
 99             continue;
100         }
101         dfs(1, 1, 0);
102         printf("%d\n", ans);
103     }
104     return 0;
105 }

 

posted @ 2016-08-16 23:00  Awner  阅读(493)  评论(0编辑  收藏  举报