题目描述

有一棵苹果树,如果树枝有分叉,一定是分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】二叉苹果树的更多相关文章

  1. 洛谷 P2015 二叉苹果树 (树上背包)

    洛谷 P2015 二叉苹果树 (树上背包) 一道树形DP,本来因为是二叉,其实不需要用树上背包来干(其实即使是多叉也可以多叉转二叉),但是最近都刷树上背包的题,所以用了树上背包. 首先,定义状态\(d ...

  2. 洛谷p2015二叉苹果树&yzoj1856多叉苹果树题解

    二叉 多叉 有一棵苹果树,如果树枝有分叉,可以是分多叉,分叉数k>=0(就是说儿子的结点数大于等于0)这棵树共有N个结点(叶子点或者树枝分叉点),编号为1~N,树根编号一定是1.我们用一根树枝两 ...

  3. 洛谷 P2015 二叉苹果树(codevs5565) 树形dp入门

    dp这一方面的题我都不是很会,所以来练(xue)习(xi),大概把这题弄懂了. 树形dp就是在原本线性上dp改成了在 '树' 这个数据结构上dp. 一般来说,树形dp利用dfs在回溯时进行更新,使用儿 ...

  4. 洛谷 P2015 二叉苹果树 && caioj1107 树形动态规划(TreeDP)2:二叉苹果树

    这道题一开始是按照caioj上面的方法写的 (1)存储二叉树用结构体,记录左儿子和右儿子 (2)把边上的权值转化到点上,离根远的点上 (3)用记忆化搜索,枚举左右节点分别有多少个点,去递归 这种写法有 ...

  5. 洛谷P2015 二叉苹果树

    题目描述 有一棵苹果树,如果树枝有分叉,一定是分2叉(就是说没有只有1个儿子的结点) 这棵树共有N个结点(叶子点或者树枝分叉点),编号为1-N,树根编号一定是1. 我们用一根树枝两端连接的结点的编号来 ...

  6. 洛谷 P2015 二叉苹果树

    老规矩,先放题面 题目描述 有一棵苹果树,如果树枝有分叉,一定是分2叉(就是说没有只有1个儿子的结点) 这棵树共有N个结点(叶子点或者树枝分叉点),编号为1-N,树根编号一定是1. 我们用一根树枝两端 ...

  7. 洛谷—— P2015 二叉苹果树

    https://www.luogu.org/problem/show?pid=2015 题目描述 有一棵苹果树,如果树枝有分叉,一定是分2叉(就是说没有只有1个儿子的结点) 这棵树共有N个结点(叶子点 ...

  8. 洛谷P2015 二叉苹果树(树状dp)

    题目描述 有一棵苹果树,如果树枝有分叉,一定是分2叉(就是说没有只有1个儿子的结点) 这棵树共有N个结点(叶子点或者树枝分叉点),编号为1-N,树根编号一定是1. 我们用一根树枝两端连接的结点的编号来 ...

  9. 洛谷P2015二叉苹果树

    传送门啦 树形 $ dp $ 入门题,学树形 $ dp $ 的话,可以考虑先做这个题. $ f[i][j] $ 表示在 $ i $ 这棵子树中选 $ j $ 个苹果的最大价值. include #in ...

  10. 洛谷 P2015 二叉苹果树 题解

    题面 裸的树上背包: 设f[u][i]表示在以u为子树的树种选择i条边的最大值,则:f[u][i]=max(f[u][i],f[u][i-j-1]+f[v][k]+u到v的边权); #include ...

随机推荐

  1. oracle & 的用法!

    /*select * from emp_bak where deptno = &"Department number" order by ename; select * f ...

  2. XHTML5 与 HTML 4.01的差异

    在 HTML 4.01 中,td 元素的 "bgcolor"."height"."width" 以及 "nowrap" ...

  3. Mysql的表名/字段名/字段值是否区分大小写

    1.MySQL默认情况下是否区分大小写,使用show Variables like '%table_names'查看lower_case_table_names的值,0代表区分,1代表不区分. 2.m ...

  4. delphi使用SQL的教程4(使用Params属性为参数赋值 )

    17.4.1 使用Params属性为参数赋值   TQuery部件具有一个Params属性,它们在设计时不可用,在程序运行过程中可用,并且是动态建立的,当为TQuery部件编写动态SQL 语句时, D ...

  5. sublime text3 php开发必要的插件

    一.安装Sublime Text 3 官网 http://www.sublimetext.com/3 一定要选择ST3,而不是ST2,3比2好用,真的,后面你就知道了. 选择对应的版本安装.完事后,要 ...

  6. PC和FPGA间的串口通信实现

    应用笔记 V1.0 2015/03/26 PC和FPGA间的串口通信实现   概述   本文将介绍PC和FPGA间的串口通信实现的基本思路和Verilog代码,对于通信而言,收发双方都要有相应的控制. ...

  7. 【Python】tuple and list 练习

    Tuple: 1.       __add__ a = (,,,,,,,,,) b = (,,,,,,,), c = (,,,,), result = a.__add__(b) print(resul ...

  8. HDU2486_A simple stone game

    这个题目是这样的,一堆石子有n个,首先第一个人开始可以去1-(n-1)个,接下来两人轮流取石子,每个人可取的石子数必须是一个不超过上一次被取的石子的K倍的整数. 现在求对于一堆数量为n的石子是否为必胜 ...

  9. 高rong效chang的可持久化treap

    很多人觉得可持久化treap很慢,但是事实上只是他们可持久化treap的写法不对.他们一般是用split和merge实现所有功能,但是这样会有许多不必要的分裂.其实我们可以用一种特殊的方式来实现插入和 ...

  10. 表格隔行变色_CSS实现鼠标悬停高亮

    <!doctype html> <html> <head> <meta http-equiv="content-type" content ...