本题是给定一棵树(n, n - 1),试问最少去掉多少条边能够得到一个节点数恰为m(1≤ m ≤ n)的子树(连通块)。
这是一道动态规划题,也就是说最终的最优解(答案)需要靠子问题的状态转移来合成。
考虑一颗根节点为u的树(T),u的子节点集合为 V = { v1, ...,vk }。
我们试着推导通过对T进行删边操作得到一颗节点数为i的子树T1所删的边的最小数目dp[u][i],其中u ∈ T1。
则有:
①若T1 ∩ T(v1) ≠ ∅,则dp[u][i] = min{ dp[v1][j] + dp[u][i - j] }, 0 ≤ j ≤ i。
②若 T1 ∩ T(v1) = ∅, 则 dp[u][i] = 1 + dp[u][i]。
实际上在递归过程中相当于把各个子树当成物品求价值后用背包的方式更新父节点的最优值。
http://poj.org/problem?id=1947
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 using namespace std; 5 const int maxn = 150 + 10; 6 const int inf = 0x3f3f3f3f; 7 int dp[maxn][maxn]; 8 int n, m, N; 9 int cnt[maxn]; 10 struct Edge{ 11 int to, next; 12 }edge[maxn]; 13 int root; 14 int head[maxn]; 15 16 void addEdge(int u, int v){ 17 edge[N].next = head[u]; 18 edge[N].to = v; 19 head[u] = N++; 20 } 21 22 void dfs(int u){ 23 //assume that T(u) is an isolated tree 24 dp[u][1] = 0; 25 for(int i = head[u]; i + 1; i = edge[i].next){ 26 int v = edge[i].to; 27 dfs(v); 28 for(int j = m; j >= 1; j--){ 29 int tem = dp[u][j] + 1; 30 for(int k = 1; k < j; k++){//spilt j 31 tem = min(tem, dp[u][k] + dp[v][j - k]); 32 } 33 dp[u][j] = tem; 34 } 35 } 36 } 37 38 int main(){ 39 while(~scanf("%d%d", &n, &m)){ 40 N = 0; 41 memset(head, -1, sizeof head); 42 memset(cnt, 0, sizeof cnt); 43 for(int i = 1, x, y; i < n; i++){ 44 scanf("%d%d", &x, &y); 45 ++cnt[y]; 46 addEdge(x, y); 47 } 48 for(int i = 1; i <= n; i++){ 49 if(!cnt[i]){ 50 root = i; 51 break; 52 } 53 } 54 memset(dp, inf, sizeof dp); 55 dfs(root); 56 int ans = dp[root][m]; 57 for(int i = 1; i <= n; i++) ans = min(ans, dp[i][m] + 1); 58 printf("%d\n", ans); 59 } 60 return 0; 61 }
原文:http://www.cnblogs.com/astoninfer/p/4872490.html