洛谷P2015 二叉苹果树(树状动归)

该博客介绍了洛谷P2015题目的详细解析,涉及一棵二叉树结构的苹果树,树枝分叉均为2叉。题目要求在限制保留的树枝数量下,计算最多能保留多少苹果。输入包括树的节点数、需保留的树枝数及每根树枝的连接节点和苹果数。输出为最多能留住的苹果总数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述

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

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

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

2 5 \ / 3 4 \ / 1 现在这颗树枝条太多了,需要剪枝。但是一些树枝上长有苹果。

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

输入输出格式

输入格式:

第1行2个数,N和Q(1<=Q<= N,1<N<=100)。

N表示树的结点数,Q表示要保留的树枝数量。接下来N-1行描述树枝的信息。

每行3个整数,前两个是它连接的结点的编号。第3个数是这根树枝上苹果的数量。

每根树枝上的苹果不超过30000个。

输出格式:

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

输入输出样例

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

这道题有个很大的坑。由于所给的数据只表明了两个点相连,并没有说哪个是父节点哪个是子节点。所以我建了一个邻接表,并把每个数据建两个边。这样子节点一定有一条边连接到父节点,具体是第几条边由建表的方式和输入数据的先后决定。在样例数据中,连接到父节点的这条边都是子节点的最后一条边,所以对于二叉树来说,只把前两条边拿出来,并不影响结果。但是其他的测试数据中,连接到父节点的边很可能不是最后一个,这样就会导致结果错误。所以在动归的过程中,要判断由子节点搜到的点是不是父节点,如果是,要跳过这条边,那么下一个就一定是子节点的子节点。
代码:
#include <iostream>
#define MAX_N 1000
using namespace std;
struct type
{
    int adjvex,nextarc;
    long int value;
} edge[MAX_N];
int cnt,head[MAX_N],father[MAX_N];
long int dp[MAX_N][MAX_N],value[MAX_N];
//dp[root][num]表示以root为根节点的子树保留num个节点的最大值
void AddEdge(int x,int y,long int v)
{
    ++cnt;
    edge[cnt].nextarc=head[x];//next指向下一条边
    edge[cnt].adjvex=y;//adjvex为当前边的终点
    head[x]=cnt;//当前边被设为顶点x的第一条边
    edge[cnt].value=v;
}
void TDP(int root,int num)
{
    if(num==0)//不保留枝条
        dp[root][num]=0;
    else if(edge[head[root]].nextarc==0)//该节点为叶子节点
        dp[root][num]=value[root];
    else
    {
        //father数组保存子节点的父节点
        //因为该二叉树由无向图得来,子节点可以通过边搜到父节点
        //以下过程用来跳过子节点连接父节点的那条边
        //这条边可能出现在子节点所连接边的任何一个位置,有添加边的顺序决定
        int arc=head[root];//arc为root节点的第一条边
        if(edge[arc].adjvex==father[root])
            //如果arc的终点为root节点的父节点
            arc=edge[arc].nextarc;//arc向后走一个,找到下一条边
        int lchild=edge[arc].adjvex;
        //左孩子结点为arc的终点
        father[lchild]=root;//左孩子结点的父节点设为root
        value[lchild]=edge[arc].value;
        arc=edge[arc].nextarc;//arc再向后走一个,找到下一条边作为root的右孩子节点
        if(edge[arc].adjvex==father[root])
            //同上,防止该边连到root的父节点
            arc=edge[arc].nextarc;
        int rchild=edge[arc].adjvex;
        father[rchild]=root;
        value[rchild]=edge[arc].value;
        for(int i=0; i<num; ++i)
        {
            if(dp[lchild][i]==0)
                TDP(lchild,i);
            if(dp[rchild][num-1-i]==0)
                TDP(rchild,num-1-i);
            dp[root][num]=max(dp[root][num],dp[lchild][i]+dp[rchild][num-1-i]+value[root]);
        }
    }
}
int main()
{
    int N,Q;
    cin>>N>>Q;
    for(int i=0; i<N-1; ++i)
    {
        int x,y;
        long int v;
        cin>>x>>y>>v;
        AddEdge(x,y,v);//x,y不确定哪个是父节点那个是子节点
        AddEdge(y,x,v);
    }
    //由题目知,二叉树根节点一定是1
    //Q+1表示保留Q条树枝,即保留Q+1个顶点
    TDP(1,Q+1);
    cout<<dp[1][Q+1];
    return 0;
}
个人网站:https://jqh.zone  阅读原文
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值