洛谷P2015 二叉苹果树

sloj P10153. 「一本通 5.2 例 1」二叉苹果树

P2015 二叉苹果树

题目描述

有一棵苹果树,如果树枝有分叉,一定是分二叉(就是说没有只有一个儿子的结点)

这棵树共有N个结点(叶子点或者树枝分叉点),编号为1N,树根编号一定是1

我们用一根树枝两端连接的结点的编号来描述一根树枝的位置。下面是一颗有4个树枝的树:

2   5
 \ / 
  3   4
   \ /
    1

现在这颗树枝条太多了,需要剪枝。但是一些树枝上长有苹果。

给定需要保留的树枝数量,求出最多能留住多少苹果。

输入格式

第一行2个整数NQ,分别表示表示树的结点数,和要保留的树枝数量。

接下来N1行,每行3个整数,描述一根树枝的信息:前2个数是它连接的结点的编号,第3个数是这根树枝上苹果的数量。

输出格式

一个数,最多能留住的苹果的数量。

输入输出样例

输入 #1
5 2
1 3 1
1 4 10
2 3 20
3 5 20
输出 #1
21

说明/提示

1<=Q<N<=100,每根树枝上的苹果<=3×104。 

我乍一看,貌似可以记忆化搜索,嗯对,确实可以

不过呢,我想作死挑战极限,所以我就写了个极其让人秃头普通的树形dp

本题权值在边上,这对思考问题有些别扭,其实只要把边的权值转移到儿子结点上,问题仍然性质不变。
需要保留的树枝数量为 M 条,即保留结点数目 j=M+1 个。树根必须保留,可以分三种情况讨论:
• 树根的左子树为空,全部保留右子树,右子树中保留 j-1 个结点;这种可以合并到第三种
• 树根的右子树为空,全部保留左子树,左子树中保留 j-1 个结点;这种也可以合并到第三种
• 树根的两棵子树非空,设左子树保留 k 个结点,则右子树保留 j-k-1 个结点。就这种有用
前两种情况就是第三种取k=j时和k=0时,但是分三种对于后面简单一点
很明显,要想保留树根时的苹果数最大,只需要求出上述三个方案中的最大值即可。
设树根为 i,左儿子为 ch[i][0], 右儿子为 ch[i][1];
对于 (1) 方案,要取得该方案的最大值,需要取得以 ch[i][1] 为根,保留 j-1 个结点的最大值。
这时同样具有上述三种方案。
其他 (2)(3) 情况同理;
由此可以看出,该问题具有明显的最优子结构性质,每个问题都与左右儿子结点有关系,但
不与孙子结点发生关系,具备无后效性;且计算方案时,搜索子结构时具备重叠性,可以用动态规划来解决。
看了算法标签之后才想到的
设 f[i,j] 表示以 i 为根的子树上保留 j 个节点时最多能保留的苹果数量。设 ch[i,0],ch[i,1] 分别为i 节点的左右孩子。
状态转移方程:这不难吧,狗头保命
f[i, j] = max{f[ch[i, 0], k] + f[ch[i, 1], j − k − 1] + a[i]}(0 <= k <= j − 1)
初始化:f[i,j]=0;(j=0,即表示以 i 为根选 0 个结点);
f[i,j]=a[i];(ch[i,0]=0 且 ch[i,1]=0, 即叶子结点时)
Answer=f[1,M+1];
注意:本题知道根为 1;若不知道根结点需要枚举根节点,再建立树;
那么又到了你们最喜欢的环节
上 代 码
复制代码
#include<bits/stdc++.h>
using namespace std;
int n,q,g[110][110],ch[110][10],f[110][110],a[110];
void dfs(int x){
    for(int i = 1;i<=n;i++){
        if(g[x][i]>=0){
            if(!ch[x][0]){
                ch[x][0] = i;
                a[i] = g[x][i];
                g[i][x] = g[x][i] = -1;
                dfs(ch[x][0]);
            }else{
                ch[x][1] = i;
                a[i] = g[x][i];
                g[i][x] = g[x][i] = -1;
                dfs(ch[x][1]);
            }
        }
    }
}
int dp(int i,int j){
    if(i==0||j==0)
        return 0;
    if(ch[i][0]==0&&ch[i][1]==0)
        return a[i];
    if(f[i][j]>0)
        return f[i][j];
    for(int k = 0;k<j;k++)
        f[i][j] = max(f[i][j],dp(ch[i][0],k)+dp(ch[i][1],j-k-1)+a[i]);
    return f[i][j];
}
int main(){
    scanf("%d%d",&n,&q);
    q++;
    for(int i = 1;i<=n;i++)
        for(int j = 1;j<=n;j++)
            g[i][j] = -1;
    for(int i = 1;i<n;i++){
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        g[x][y] = g[y][x] = z;
    }
    dfs(1);
    printf("%d",dp(1,q));
    return 0;
}
复制代码

 

posted @   cztq  阅读(78)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话
点击右上角即可分享
微信分享提示