poj 1947 Rebuilding Roads 树形动态规划,由左向右合并子树
对于一颗以 rt 为根的树,其有 I 个子节点分别为 S1,S2,。。。,SI ,则
状态
dp(i,j)表示前 1,2,,,,,I 子树,组合而成一棵 包含 J个节点的 子树的最小花费
sum(Si) 表示 以 Si 节点为根的子树,节点数量
Cut (Si, K) 表示 子树 Si 中取得 一棵包含 根Si 的子树 且节点数量为 K 的最小花费
则状态转移方程:
其中 x <= Sum(Si), 且
注意到, 其实我们已经把 子树S1,S2,,S(i-1) 合并到了 dp(I-1)里头去了。这样就简化了枚举计算。
另外,还可以注意到
所以我们可以递归从 子节点往 上处理。
另外,在编码时要注意,叶子节点处理,以及 若当前子树 取0 则 花费为1,因为与父节点相连的边需要舍弃。
更要注意的是, 在合并过程中需要 将中间结果 保存在辅助 数组 F[] 中, 不能直接覆盖 dp[] , 以为在计算过程需要反复用到,
否则会出错, 笔者在这里 WA了半天
给出一组 关于这个Bug 的测试数据
12 7
1 2
1 3
1 4
2 5
3 6
3 7
4 8
4 9
5 10
10 11
10 12
正确结果为 1
解题代码
View Code
// Code by yefeng1627 // Time: 2013-1-17 #include<stdio.h> #include<string.h> #include<stdlib.h> #include<algorithm> #include<iostream> #include<vector> using namespace std; const int inf = 0x3f3f3f3f; const int N = 155; vector< int > Q[N]; struct node { int dp[N], sum; }T[N]; int n, P, ans; void Input() { for(int i = 0; i <= n; i++) Q[i].clear(); int x, y; for(int i = 1; i < n; i++) { scanf("%d%d", &x,&y); Q[x].push_back(y); } } // dp(i,j) 表示当前以rt为根,前i个子树组合一颗含j个节点的最小花费 void dfs( int rt ) // cost 为由父节点到当前子树的路径长度 { //初始化dp方程,皆为无穷大 memset( T[rt].dp, 0, sizeof( T[rt].dp ) ); T[rt].sum = 1; //当前子树,节点数量总和 // Dp方程初始化 //首先处理其子树,得到子树的 cut,与 sum 信息 for(int i = 0; i < Q[rt].size(); i++) { int v = Q[rt][i]; dfs( v ); T[rt].sum += T[v].sum; } //处理当前以rt为根树,子节点组合情况 int tot = 1; T[rt].dp[0] = 1; //子树上全部截掉时,从根节点处破坏路径,花费1 for(int i = 1; i <= Q[rt].size(); i++) { // dp[i][j],表示前 i棵子树,构成一棵节点数量为J的子树(包括根节点)的最小花费 int v = Q[rt][i-1]; int f[N]; for(int j = 0; j <= tot; j++) f[j] = T[rt].dp[j]; // 枚举组合子树节点数量 J , j至少为1,因为包含根节点rt for(int j = 1; j <= tot; j++) { //处理第v棵子树,枚举此子树贡献节点数量x, 且x <= T[v].sum, j-x >= 1 int tmp = inf; for(int x = 0; (x <= T[v].sum ) && (j-x>=1) ; x++ ) tmp = min( tmp, T[rt].dp[j-x] + T[v].dp[x] ); // take into considerations f[j] = min( T[rt].dp[j] + 1, tmp ); } for(int j = tot+1; j <= tot+T[v].sum; j++) T[rt].dp[j] = T[v].dp[j-tot]; // assention dp[] has change.... for(int j = 0; j <= tot; j++) T[rt].dp[j] = f[j]; tot += T[v].sum; } if( tot >= P ) ans = min( ans, T[rt].dp[P] + 1 ); } int main() { while( scanf("%d%d",&n,&P) != EOF) { Input(); ans = inf; dfs(1); ans = min( ans, T[1].dp[P] ); printf("%d\n", ans ); } return 0; }