【SDOI2008】山贼集团

这是一道树形dp和状压dp结合的题目,思考量和代码细节都不少。

我们首先定义f[i][j]表示在以i为根的子树当中,建立分部的节点状压之后为j的最大收益

那么转移是显然的,定义k为i的儿子,那么f[i][j] = max(f[k][l] + f[i][j ^ l] + val). 其中val表示其他的收益

我们可以预处理val数组,剩下的就是细节问题了

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 inline int read() {
 4     int ret = 0, op = 1;
 5     char c = getchar();
 6     while (c < '0' || c > '9') {
 7         if (c == '-') op = -1;
 8         c = getchar();
 9     }
10     while (c >= '0' && c <= '9') {
11         ret = ret * 10 + c - '0';
12         c = getchar();
13     }
14     return ret * op;
15 }
16 int n, p;
17 struct node {
18     int next, to; 
19 } a[110 << 1];
20 int head[110 << 1], num, c[110][15];
21 int f[110][1 << 15], val[1 << 15];
22 inline void add(int from, int to) {
23     a[++num].next = head[from];
24     a[num].to = to;
25     head[from] = num;
26 }
27 void dfs(int now, int fa) {
28     for (register int i = head[now]; i != -1; i = a[i].next)
29         if (a[i].to != fa) {
30             dfs(a[i].to, now);
31             for (register int j = (1 << p) - 1; j; j--) {
32                 for (register int k = j; k; k = j & (k - 1))
33                     f[now][j] = max(f[now][j], f[now][j ^ k] + f[a[i].to][k]);
34             }
35         }
36     for (register int i = (1 << p) - 1; i; i--)
37         f[now][i] = f[now][i] + val[i];
38 }
39 int main() {
40     num = -1;
41     memset(head, -1, sizeof(head));
42     n = read(); p = read();
43     for (register int i = 1; i < n; ++i) {
44         int x = read(), y = read();
45         add(x, y);
46         add(y, x);
47     }
48     for (register int i = 1; i <= n; ++i) {
49         for (register int j = 0; j < p; ++j) {
50             c[i][j] = read();
51         }
52         f[i][0] = 0;
53         for (register int j = 1; j < (1 << p); ++j) {
54             int xx = j & (-j);
55             int xxx = log2(xx);
56             f[i][j] = f[i][j ^ xx] - c[i][xxx];
57         }
58     }
59     int t = read();
60     for (register int i = 1; i <= t; ++i) {
61         int v = read(), tot = read(), now = 0;
62         for (register int j = 1; j <= tot; ++j) {
63             int nn = read();
64             now |= (1 << (nn - 1));
65         }
66         val[now] += v;
67         int k = ((1 << p) - 1) ^ now;
68         for (register int l = k; l; l = (l - 1) & k) {
69             val[l | now] += v;
70         }
71     }
72     dfs(1, 0);
73     printf("%d\n", f[1][(1 << p) - 1]);
74     return 0;
75 }
AC Code

 

posted @ 2019-07-03 20:35  AD_shl  阅读(253)  评论(0编辑  收藏  举报