• poj 2486 树形DP 子树合并



    大致思路是从根节点DFS下去后,处理完子树信息后,合并然后得出根节点信息,
    但是这一题子节点信息的合并确实比较棘手.

    如果我们尝试把从当前根节点走K步得到最大苹果数,划分为如下两种:
         
      go[t][i]代表节点t的所有子树上走i步不返回,取得的最大苹果数
      bk[t][i]代表节点t的所有子树上走i步并返回,取得的最大苹果数
     
    数组二维go,bk go[t][i]代表节点t的所有子树上至多走i步不返回,取得的最大苹果数 bk[t][i]代表节点t的所有子树上至多走i步并返回,取得的最大苹果数 求节点为x,实行不断合并子树求最优值 当前合并到了q棵子树: go[x][i]就是这q棵子树上至多走i步不返回的最优值 bk[x][i]就是这q棵子树上至多走i步并返回的最优值 合并第q+1棵子树(不妨设第q+1棵子树的根为y)的时候,有 go[x][i] = max( bk[x][j]+go[y][i-j], bk[y][j],go[x][i-j] ), j=0.....i bk[x][i] = max( bk[x][j]+bk[y][i-j] ) j=0,.....i;

    关于边界的初始化问题, 对于当前以x为根的树, 因为递归处理好了其子节点J为根的子树,
    但是处理子树J的时候,把J节点看作0来处理,回朔到X节点,再处理J节点时再去计算从X走到J的苹果值.
    这样,
      若计算对于go[j][k] 则需要添加一步由x到j的路径,所以go[j][k] = go[j][k-1]+Apple[j]
      若计算对于bk[j][k] 则需要添加来回一趟,共两步从x到j,再回到x的路径,所以bk[j][k] = bk[j][k-2]+Apple[j]
    还需要注意, bk[j][1] = bk[j][0] = 0 , go[j][0] = 0, 前者无法走回,后者还没开始走,当然为0
    // Code by yefeng1627
    // Time 2013-1-17
    #include<stdio.h>
    #include<stdlib.h>
    #include<string.h>
    #include<vector>
    using namespace std;
    
    const int N = 110;
    int go[N][N<<1],bk[N][N<<1],tmp1[N<<1],tmp2[N<<1];
    int n, k, Apple[N];
    vector<int> Q[N];
    
    int max(int a,int b) {return a>b?a:b;}
    
    void DP(int x, int y )
    {
        for(int i = 0; i <= k; i++) tmp1[i]=tmp2[i]=0;    
        for(int i = k; i >= 0; i--)
            for(int j = 0; j <= i; j++)
                tmp1[i] = max(tmp1[i],max(bk[x][j]+go[y][i-j],go[x][j]+bk[y][i-j]) );
        for(int i = k; i >= 0; i--)
            for(int j = 0; j <= i; j++)
                tmp2[i] = max(tmp2[i],bk[x][j]+bk[y][i-j]);
        for(int i = 0; i <= k; i++) 
            go[x][i] = tmp1[i], bk[x][i] = tmp2[i];
    }
    void solve(int x, int fa)
    {
        int y;
        for(int i = 0; i < (int)Q[x].size(); i++ )
            if( (y=Q[x][i]) != fa )     
            {
                solve( y, x );
                for(int L = k; L >= 2; L-- ) bk[y][L] = bk[y][L-2]+Apple[y];
                bk[y][1] = bk[y][0] = go[y][0] = 0;    
                for(int L = k; L >= 1; L-- ) go[y][L] = go[y][L-1]+Apple[y];
                DP( x, y );
            }
    }
    int main()
    {
        while( scanf("%d%d",&n, &k) != EOF )
        {
            memset(go,0,sizeof(go));
            memset(bk,0,sizeof(bk));
            for(int i = 0; i < n; i++)
            {
                scanf("%d", &Apple[i] );    
                Q[i].clear();    
            }
            int a, b;
            for(int i = 0; i < n-1; i++)
            {    
                scanf("%d%d",&a,&b);
                a--; b--;
                Q[a].push_back(b);
                Q[b].push_back(a);
            }
            solve(0, -1);    
            printf("%d\n", go[0][k]+Apple[0] );        
        }
        return 0;
    }
  • 相关阅读:
    C#字符串常见处理
    应用各种领域逻辑模式组织业务逻辑层
    谈谈C#的异常处理机制
    android适配器Adapter
    Struts+Tomcat搭建
    Android特性与系统架构
    常用控件应用之文本框(TextView)特效
    Activity中与ListActivity中使用listview区别
    Android四大基本组件介绍与生命周期
    ftp原理
  • 原文地址:https://www.cnblogs.com/yefeng1627/p/2865337.html
Copyright © 2020-2023  润新知