• 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;
    }
  • 相关阅读:
    时间单位转化
    快速排序算法
    用virtualenv建立Python独立开发环境
    Shell正则表达式之grep、sed、awk实操笔记
    Objective-C 30分钟入门教程
    base64加密后字符串长度
    error: synthesized property 'name' must either be named the same as a compatible instance variable or must explicitly name an instance variable问题解决
    Ubuntu系统下通过Clang编译器编写Objective-C
    MongoDB 聚合Group(二)
    MongoDB聚合(单一用途的聚合方法)
  • 原文地址:https://www.cnblogs.com/yefeng1627/p/2864869.html
Copyright © 2020-2023  润新知