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;
}

 

posted @ 2013-01-17 17:12  yefeng1627  阅读(354)  评论(0编辑  收藏  举报

Launch CodeCogs Equation Editor