car

好,这又是一道状压DP题。

题意:给你n * n的棋盘,有m个点不能放东西,要放k个车上去,问有多少种方案可以使它们不互相攻击。

n <= 18

然后就一眼状压了。

设f[i][j]表示前i行,已经放车的列状态是j的方案数。

然后有两种转移方案。我比较喜欢刷表法。

每次枚举i,然后枚举j,然后枚举i + 1行在哪列放车。

初始状态是f[0][0] = 1,最后统计f[n][x]

时间复杂度n²logn

 1 #include <cstdio>
 2 
 3 const int N = 20, MO = 1e9 + 7;
 4 
 5 int f[N][1 << N];
 6 bool vis[N][N];
 7 
 8 inline bool check(int s, int k) {
 9     int cnt = 0;
10     while(s) {
11         cnt += (s & 1);
12         if(cnt > k) {
13             return 0;
14         }
15         s >>= 1;
16     }
17     return cnt == k;
18 }
19 
20 int main() {
21     //freopen("car.in", "r", stdin);
22     //freopen("car.out", "w", stdout);
23     int n, m, k;
24     scanf("%d%d%d", &n, &m, &k);
25     if(k > n) {
26         printf("0");
27         return 0;
28     }
29     for(int i = 1, x, y; i <= m; i++) {
30         scanf("%d%d", &x, &y);
31         vis[x][y - 1] = 1;
32     }
33 
34     f[0][0] = 1;
35 
36     int lm = 1 << n;
37     for(int i = 0; i < n; i++) {
38         for(int j = 0; j < lm; j++) {
39             f[i + 1][j] += f[i][j];
40             f[i + 1][j] %= MO;
41             for(int p = 0; p < n; p++) {
42                 if(((j >> p) & 1) || vis[i + 1][p]) {
43                     continue;
44                 }
45                 (f[i + 1][j | (1 << p)] += f[i][j]) %= MO;
46             }
47         }
48     }
49 
50     int ans = 0;
51     for(int i = 0; i < lm; i++) {
52         if(check(i, k)) {
53             ans += f[n][i];
54             ans %= MO;
55         }
56     }
57     printf("%d", ans);
58     return 0;
59 }
AC代码

 

posted @ 2018-09-21 21:02  garage  阅读(142)  评论(0编辑  收藏  举报