P2015 二叉苹果树 萌新第三个题解

题目描述

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

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

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

2   5

 \ /

  3   4

   \ /

    1

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

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

输入格式

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

接下来 N−1 行,每行 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×10

4

题目思路:

如何选择整个苹果树上二个枝条所挂的苹果数量最大化,当然题目还给了一个限制条件,必须从树根出发;显然我们只能采用深搜的方式,从树根一直搜索到树梢,然后从树梢返回过程中,来逐层计算最大值,并对最大值进行比较和保存,直到树根;一般来说,我们都会采用建双向边的方式,因为传统的思路是有去有回必定是双向边,其实这个思路会给题目增加空间复杂度,并且在这个思路之上我们还需要建一个vis数组来辅助dfs的搜索不会陷入死循环;换个思路,如果我们只建立一个有向图的话,那么我们就可以节省vis数组的使用,因为单项边是不会产生回头搜索的;这里面实质上还有一个是我们对dfs的了解多少问题,dfs本身就是一个一直往前直到终点,然后不断返回并在返回过程中计算我们所要的值,也就是dfs本身就包含了有递有归的天然属性;无需在考虑双向边实现树的递归;

代码详解:  (仅供参考)

# include <bits/stdc++.h>

using namespace std;

const int N = 105;

struct edge

{

int u,v,w,next;

}e[N*2];  //此处如果采用双向边,那么就是e[N*2];单项边就取用e[N];此处的空间可以直接省一半;

int n,q,head[N],cnt = 1,f[N][N];

bool vis[N];

void add_edge(int u,int v,int w)

{

e[cnt] = {u,v,w,head[u]};  //采用集合的写法,减少行数,简洁代码

head[u] = cnt++;

}

void dfs(int u)

{

//vis[u] = 1;   单项边无需使用vis数组;

for (int i = head[u]; i; i = e[i].next)

{

int v = e[i].v;

int w = e[i].w;

//if (vis[v]) continue; 这行其实不需要,因为本题是树,没有环,且我们使用了单项边构图

dfs(v);

for (int j = q; j >= 1; j--)

for (int h = 1; h <= j; h++)

f[u][j] = max(f[u][j],w + f[u][h-1] + f[v][j-h]);

// f[u][j] 等于一边取h-1条枝,一边取j-h条枝,再加上当前这条

}

}

int main()

{

scanf("%d%d",&n,&q);

for (int i = 1,u,v,w; i < n; i++)

{

scanf("%d%d%d",&u,&v,&w);

add_edge(u,v,w);

//add_edge(v,u,w);  本行其实不需要,也能节省一半的构图空间;

//有些人因为害怕题目数据会出反边,所以建立双向边,然而数据中没有反边

//反边也不符合本题所述的二叉树,并且我们采用的是dfs方法解题; 

}

dfs(1);

printf("%d",f[1][q]);

return 0;

 }

以上为个人见解,仅供参考;

题目扩展:如果题目要求不再限制必须从根出发来判断最大苹果数量的话,我们需要如何做?也就是对于n个点的苹果树,求相连的q个枝条,最大的苹果数量?欢迎大家指点。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值