【洛谷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 ...
随机推荐
- 团队项目-BUG挖掘
测试硬件: 华为畅享5 测试平台: 安卓5.1 测试项目Git地址: https://github.com/RABITBABY/We-have-bing 测试Apk来源地址: http://www.a ...
- (转)微软借力.NET开源跨平台支持,布局物联网平台开发
今天科技类最大的新闻,莫过于微软宣布.NET开发框架开源计划..NET 开源,集成 Clang 和 LLVM 并且自带 Android 模拟器,这意味着 Visual Studio 这个当下最好没有之 ...
- 【转】Linux C 网络编程——TCP套接口编程
地址:http://blog.csdn.net/matrix_laboratory/article/details/13669211 2. socket() <span style=" ...
- KMP算法模板(pascal)
洛谷P3375: program rrr(input,output); var i,j,lena,lenb:longint; a,b:ansistring; next:..]of longint; b ...
- 洛谷P1658 购物
题目戳 题目描述 你就要去购物了,现在你手上有N种不同面值的硬币,每种硬币有无限多个.为了方便购物,你希望带尽量少的硬币,但要能组合出1到X之间的任意值. 输入输出格式 输入格式: 第一行两个数X.N ...
- 如何实现密码输入框focus状态弹出提示信息
一.密码输入提示框样式实现 效果图如下: 源码如下: <html> <style type="text/css"> *{ padding: 0; margi ...
- 【BZOJ4945】【NOI2017】游戏(搜索,2-sat)
[NOI2017]游戏(搜索,2-sat) 题面 BZOJ的SPJ是假的 兹磁洛谷 题解 如果没有\(x\)地图的影响 这就是一个裸的\(2-sat\)问题 但是现在有不超过\(8\)个\(x\)地图 ...
- bzoj5118: Fib数列2(费马小定理+矩阵快速幂)
题目大意:求$fib(2^n)$ 就是求fib矩阵的(2^n)次方%p,p是质数,根据费马小定理有 注意因为模数比较大会爆LL,得写快速乘法... #include<bits/stdc++.h& ...
- 【bzoj4002】有意义的字符串
Portal --> bzoj4002 Solution 虽然说这题有点强行但是感觉还是挺妙的,给你通项让你反推数列的这种==有点毒 补档时间 首先有一个东西叫做特征方程,我们可以 ...
- Spring MVC POJO入参过程分析
SpringMVC确定目标方法POJO类型的入参过程 1.确认一个key: (1).若目标方法的POJO类型的参数没有使用@ModelAttribute作为修饰,则key为POJO类名第一个字母的小写 ...