【BZOJ2466】树

题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=2466


 

并不算简单的树形DP,虽然状态的设计很符合树形DP的套路。。。

设f[x][0]表示按x且x灭,x的所有子孙均亮的最少按按钮次数,1则是x亮;g[x][0]表示不按x且x灭,x的所有子孙均亮的最少按按钮次数。

树形DP常常设dp[x]表示x子树的最优值,若无法转移,则需要设成dp[x][k]表示x子树,附加信息为k的最优值。

下面考虑一下转移,假如按x,那么其儿子均需灭,若不按,则其儿子均需亮;可以认为x亮是因为他和他儿子中有奇数个点被按,x灭是因为有偶数个点被按,然后就可以理解转移方程了,详见代码。

然后别忘了对于叶子节点的处理,若x为叶子结点,则f[x][0]和g[x][1]是不可能的,因此可以设为n+1,此外还有f[x][1]=1,g[x][0]=0。

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 
 5 using namespace std;
 6 
 7 const int maxn = 105;
 8 
 9 int head[maxn], eid;
10 
11 struct Edge {
12     int v, next;
13 } edge[2 * maxn];
14 
15 inline void insert(int u, int v) {
16     edge[++eid].v = v;
17     edge[eid].next = head[u];
18     head[u] = eid;
19 }
20 
21 int n, f[maxn][2], g[maxn][2];
22 //f表示按,g表示不按,0表示灭,1表示亮,而该点所有子孙均亮(如果有)的最少按按钮次数
23 
24 void dfs(int u, int fa) {
25     f[u][0] = g[u][1] = n + 1, f[u][1] = 1, g[u][0] = 0;
26     //对于叶子结点的处理
27     for (int p = head[u]; p; p = edge[p].next) {
28         int v = edge[p].v;
29         if (v == fa) continue;
30         dfs(v, u);
31         int f0 = f[u][0], f1 = f[u][1], g0 = g[u][0], g1 = g[u][1];
32         f[u][0] = min(f0 + g[v][0], f1 + f[v][0]);
33         f[u][1] = min(f0 + f[v][0], f1 + g[v][0]);
34         g[u][0] = min(g0 + g[v][1], g1 + f[v][1]);
35         g[u][1] = min(g0 + f[v][1], g1 + g[v][1]);
36     }
37 }
38 
39 int main() {
40     while (1) {
41         scanf("%d", &n);
42         if (!n) return 0;
43         memset(head, 0, sizeof(head));
44         eid = 0;
45         for (int i = 1; i < n; ++i) {
46             int u, v;
47             scanf("%d%d", &u, &v);
48             insert(u, v);
49             insert(v, u);
50         }
51         dfs(1, 0);
52         printf("%d\n", min(f[1][1], g[1][1]));
53     }
54     return 0;
55 }
AC代码

 

posted @ 2018-11-02 14:05  Mr^Kevin  阅读(111)  评论(0编辑  收藏  举报