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 }