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;

}

 

你可能感兴趣的:(Build)