【洛谷P2015】二叉苹果树
题目描述
有一棵苹果树,如果树枝有分叉,一定是分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
算法:
树形DP
分析:
这道题其实是要我们计算在一棵二叉树上保留一定数量的枝条情况下的最大权值。
这是一个基础的树形DP模板题,在二叉树上进行动规。我们发现对于树上的任意一棵子树,它的根节点和两个子树存在着关系,我们探究一下,树是由递归性质得到的,所以我们首先要把我们得到的数据先在一个数组上建造出来。
把树建好了之后,我们就可以探究它的状态转移方程,我们已知n-1条边,为了方便,我们把它设为双向的边。我设len[x][y]和len[y][x]表示x到y的边上权值为len[x][y](或len[y][x])。
接下来,设num[v]表示以v为子节点的那条边的长度,设f[v][k]为以v为根节点的子树中保留k条边的最大权值,设tr[v][ans]为以v为根节点的左右儿子分别是什么,当ans表示1时,他是左儿子,反之,则为右儿子。
把一棵树放到一个线性DP里面想的话,就会容易多了。我要求当前的f[v][k],我首先要求得他的左右儿子分担一共k条边的最大值分别是什么,然后相加即可。
可列得状态转移方程为:f[v][k]=max(f[v][k],f[tr[v][1]][i]+f[tr[v][2]][k-i-1]+num[v]);
这个东西相信不难看懂,接下来我们需要优化程序。既然树可以递归来建造,那么我们是不是也可以用递归来求解呢?嗯,是可以的。
这是我们就可以将普通的DP放到DFS上做,合成了记忆化搜索。因为有一些枝条我在之前已经计算过了,那么我可以直接拿来用。
说几点注意的,最后输出答案要输出q+1的情况,因为在计算过程中我们为了方便,将边的问题都转化成父节点或者是子节点问题,所以需要我们在节点数上再+1。
切记len数组要初始化一个负数,在用完其中一条边时,记得把那条边和另外一条等价的边都给删掉。
上代码:
#include<cstdio>
#include<iostream>
using namespace std; int n,q,tr[][],num[],len[][],f[][]; //数据规模不大,int 型已经足够 inline int read() //读入优化
{
int x=,f=;
char c=getchar();
while (c<||c>)
f=c=='-'?-:,c=getchar();
while (c>=&&c<=)
x=(x<<)+(x<<)+(c^),c=getchar();
return x*f;
} void buildtree(int v) //建树
{
int i,ans=;
for (i=;i<=n;i++)
if (len[v][i]>=) //有分叉
{
ans++; //记录并判断左右子树
tr[v][ans]=i;
num[i]=len[v][i]; //记录长度
len[v][i]=len[i][v]=-; //删除两条边
buildtree(i); //递归它的孩子节点
if (ans==) //到了就返回
return;
}
} void dfs(int v,int k) //记忆化搜索
{
if (k==) //边界
f[v][k]=;
else
if (tr[v][]==&&tr[v][]==) //到了叶子节点,其值等于原值
f[v][k]+=num[v];
else
{
f[v][k]=; //先清零
int i,j;
for (i=;i<k;i++) //枚举0到k-1的情况
{
if (!f[tr[v][]][i]) //左儿子记忆化
dfs(tr[v][],i);
if (!f[tr[v][]][k-i-]) //右儿子记忆化
dfs(tr[v][],k-i-);
f[v][k]=max(f[v][k],f[tr[v][]][i]+f[tr[v][]][k-i-]+num[v]); //状态转移
}
}
} int main()
{
int i,j;
n=read();
q=read();
for (i=;i<=n;i++) //初始化
for (j=;j<=n;j++)
len[i][j]=-;
for (i=;i<=n-;i++)
{
int x=read(),y=read();
len[x][y]=len[y][x]=read();
}
buildtree(); //建树
dfs(,q+); //求解
printf("%d",f[][q+]); //注意,切记是q+1
return ;
}
这是一道二叉树的基础DP,我感觉挺好理解的,主要的模型就是递归建树+记忆化搜索DP,简单记为2个DFS。
嗯,就这样了。
【洛谷P2015】二叉苹果树的更多相关文章
- 洛谷 P2015 二叉苹果树 (树上背包)
洛谷 P2015 二叉苹果树 (树上背包) 一道树形DP,本来因为是二叉,其实不需要用树上背包来干(其实即使是多叉也可以多叉转二叉),但是最近都刷树上背包的题,所以用了树上背包. 首先,定义状态\(d ...
- 洛谷p2015二叉苹果树&yzoj1856多叉苹果树题解
二叉 多叉 有一棵苹果树,如果树枝有分叉,可以是分多叉,分叉数k>=0(就是说儿子的结点数大于等于0)这棵树共有N个结点(叶子点或者树枝分叉点),编号为1~N,树根编号一定是1.我们用一根树枝两 ...
- 洛谷 P2015 二叉苹果树(codevs5565) 树形dp入门
dp这一方面的题我都不是很会,所以来练(xue)习(xi),大概把这题弄懂了. 树形dp就是在原本线性上dp改成了在 '树' 这个数据结构上dp. 一般来说,树形dp利用dfs在回溯时进行更新,使用儿 ...
- 洛谷 P2015 二叉苹果树 && caioj1107 树形动态规划(TreeDP)2:二叉苹果树
这道题一开始是按照caioj上面的方法写的 (1)存储二叉树用结构体,记录左儿子和右儿子 (2)把边上的权值转化到点上,离根远的点上 (3)用记忆化搜索,枚举左右节点分别有多少个点,去递归 这种写法有 ...
- 洛谷P2015 二叉苹果树
题目描述 有一棵苹果树,如果树枝有分叉,一定是分2叉(就是说没有只有1个儿子的结点) 这棵树共有N个结点(叶子点或者树枝分叉点),编号为1-N,树根编号一定是1. 我们用一根树枝两端连接的结点的编号来 ...
- 洛谷 P2015 二叉苹果树
老规矩,先放题面 题目描述 有一棵苹果树,如果树枝有分叉,一定是分2叉(就是说没有只有1个儿子的结点) 这棵树共有N个结点(叶子点或者树枝分叉点),编号为1-N,树根编号一定是1. 我们用一根树枝两端 ...
- 洛谷—— P2015 二叉苹果树
https://www.luogu.org/problem/show?pid=2015 题目描述 有一棵苹果树,如果树枝有分叉,一定是分2叉(就是说没有只有1个儿子的结点) 这棵树共有N个结点(叶子点 ...
- 洛谷P2015 二叉苹果树(树状dp)
题目描述 有一棵苹果树,如果树枝有分叉,一定是分2叉(就是说没有只有1个儿子的结点) 这棵树共有N个结点(叶子点或者树枝分叉点),编号为1-N,树根编号一定是1. 我们用一根树枝两端连接的结点的编号来 ...
- 洛谷P2015二叉苹果树
传送门啦 树形 $ dp $ 入门题,学树形 $ dp $ 的话,可以考虑先做这个题. $ f[i][j] $ 表示在 $ i $ 这棵子树中选 $ j $ 个苹果的最大价值. include #in ...
- 洛谷 P2015 二叉苹果树 题解
题面 裸的树上背包: 设f[u][i]表示在以u为子树的树种选择i条边的最大值,则:f[u][i]=max(f[u][i],f[u][i-j-1]+f[v][k]+u到v的边权); #include ...
随机推荐
- Java第一次笔记
- 《高性能JavaScript》学习笔记——日更中
------------------2016-7-20更------------------ 最近在看<高性能JavaScript>一书,里面当中,有讲很多提高js性能的书,正在看的过程中 ...
- Jmeter 快速入门--简单的http压测
1.添加线程组 打开jmeter主窗口后,选择左侧树形结构里的"测试计划",然后右键选择添加,选择"threads(users)",选择"线程组&qu ...
- MAVEN pom.xml 解读
POM全称是Project Object Model,即项目对象模型.pom.xml是maven的项目描述文件,它类似与antx的project.xml文件.pom.xml文件以xml的 形式描述项 ...
- 将oracle数据库表使用命令的形式导入到excle文件中 亲测可用!
main.sql 中的代码 set markup html on entmap ON spool on preformat off spool D:\新建文件夹\mick\tables.xls @ge ...
- C# 开发人员的函数式编程
摘要:作为一名 C# 开发人员,您可能已经在编写一些函数式代码而没有意识到这一点.本文将介绍一些您已经在C#中使用的函数方法,以及 C# 7 中对函数式编程的一些改进. 尽管 .NET 框架的函数式编 ...
- Java中TimeZone类的常用方法
一.TimeZone类的定义 TimeZone类是一个抽象类,主要包含了对于时区的各种操作,可以进行计算时间偏移量或夏令时等操作 二.TimeZone类的常用方法 1.getAvailableIDs( ...
- Spring Boot系列教程三:使用devtools实现热部署
一.前言 Eclipse下使用spring-tool-suite插件创建一个spring boot 工程,通过右键“Run As”--->"Spring Boot App"来 ...
- 十大最佳Leap Motion体感控制器应用
十大最佳Leap Motion体感控制器应用 Leap Motion Controller也许还没有准备好大规模的发售,但是毫无疑问,这款小巧的动作捕捉器是我们见过的最酷的设备之一.这款设备的硬件 ...
- CF891E [数学题]
1.答案=初始乘积-最终乘积的期望.然后直接dp+ntt是O(nklogk) 2.考虑展开式子ans=sum(a[i]-b[i]),大概感受一下未知数个数相同的项系数相同,问题在于如何求系数 3.没思 ...