题目链接: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 ", min(f[1][1], g[1][1])); 53 } 54 return 0; 55 }