POJ 1947 Rebuilding Roads

题意:给一棵含有n个节点的树,问最少切割多少条边,能够得到一棵恰好包含p个节点的子树。

解法:设d[x][i]表示以x为根的子树,切割出含有i个节点的子树最少需要切割多少条边。对于每个子节点y,可以直接将其切掉,也可以对以y为根的树进行切割,然后给以x为根的树增加节点数。这个地方其实就是一个背包,好好体会。

   状态转移方程为d[x][i] = max(d[x][i]+1, d[x][i-j] + d[y][j])。

   注意枚举的时候要逆序,否则要多开一维数组。

tag:树形dp, 背包

 1 /*
 2  * Author:  Plumrain
 3  * Created Time:  2013-11-19 16:11
 4  * File Name: DP-POJ-1947-2.cpp
 5  */
 6 #include <iostream>
 7 #include <cstdio>
 8 #include <cstring>
 9 #include <vector>
10 
11 using namespace std;
12 
13 #define CLR(x) memset(x, 0, sizeof(x))
14 #define PB push_back
15 const int maxint = 2147483647 / 10;
16 
17 int n, p, f;
18 int d[200][200];
19 bool vis[200];
20 vector<int> son[200];
21 
22 void init()
23 {
24     for (int i = 1; i <= n; ++ i)
25         son[i].clear();
26     
27     CLR (vis);
28     int t1, t2;
29     for (int i = 0; i < n-1; ++ i){
30         scanf ("%d%d", &t1, &t2);
31         vis[t2] = 1; 
32         son[t1].PB (t2);
33     }
34         
35     for (int i = 1; i <= n; ++ i)
36         if (!vis[i]){
37             f = i; break;
38         }
39 }
40 
41 void dfs(int x)
42 {
43     int sz = son[x].size();
44     for (int i = 1; i <= p; ++ i)
45         d[x][i] = maxint;
46     d[x][1] = 0;
47 
48     for (int t = 0; t < sz; ++ t){
49         int v = son[x][t];
50         dfs(v);
51         for (int i = p; i >= 1; -- i){
52             d[x][i] += 1;
53             for (int j = 1; j < i; ++ j)
54                 d[x][i] = min(d[x][i], d[x][i-j] + d[v][j]);
55         }
56     }
57 }
58 
59 int main()
60 {
61     while (scanf ("%d%d", &n, &p) != EOF){    
62         init();
63         
64         dfs(f);
65         
66         d[f][p] -= 1;
67         int ans = maxint;
68         for (int i = 1; i <= n; ++ i)
69             ans = min(ans, d[i][p]);
70         printf ("%d
", ans + 1);
71     }
72     return 0;
73 }
View Code
------------------------------------------------------------------
现在的你,在干什么呢?
你是不是还记得,你说你想成为岩哥那样的人。
原文地址:https://www.cnblogs.com/plumrain/p/POJ_1947.html