初学树型dp
树型DP
DFS的回溯是树形DP的重点以及核心,当回溯结束后,root的子树已经被遍历完并处理完了。这便是树形DP的最重要的特点
自己认为应该注意的点
- 好多人都说在更新当前节点时,它的儿子结点都给更新完了,实际上这并不准确。对于当前节点,我们需要dfs它的儿子,并且在dfs中进行dp。在此过程中并不是等到儿子都更新完我们才更新当前节点的信息(假设当前节点为x, 有儿子son1 , son2, son3, 且son1已经更新完了, 即x已有了son1的信息, son2刚刚更新完,即dfs正在son2的位置回溯), 我们拿着son2, x和son1的信息再次更新x, 如此,x才有了son1,son2的综合信息,之后再从son3 dfs进去找son3的信息,最后才得到x的信息。
- 需要注意枚举的顺序,树型dp有点像01背包,而01背包更新信息时你如果没有记录对于i,i中前j个的状态,你就需要倒序枚举,而树型dp中通常直接用f[x] [...]...表示x所在子树的信息,这里枚举k的时候就需要像01背包一样了,从size[x]逆序枚举。
(01背包倒着写时要倒序,不然就表示可以多次使用前面的物品更新后面的物品的状态(比如你顺着写,你第j个物品用到了前j个物品来更新,那当你再用第j个物品更新第j+1个物品时,又把前面的算了一遍,所以就算重复了),这不就成了完全背包嘛,我们的01背包倒着写是为了每次更新,都是取用它前面的状态,而它前面的状态又是没改过的,所以不会选重。树型dp同理,用前j个儿子更新第j+1个儿子时不能选重复)
梨提
luoguP1352 没有上司的舞会(熟悉一下回溯)
https://www.luogu.org/problemnew/show/P1352
/*
f[i] [0/1] 0与1分别表示第i个人不去,去时的最大快乐指数
所以 f[i] [0] += max( f[son] [1] ,f[son] [0]);
f[i] [1] += max(f[son] [0] , 0)
为什么f[i][1] 要与0 做比较? : 因为快乐指数可能是负的
为什么f[i][0] 不用与0作比较,直接=max?:就算是负的,也只能直接去最大的,他又不去...
为什么是+=? : 下面有
*/
#include <cstdio>
#include <algorithm>
using namespace std;
const int MAXN = 6000+9;
int n,cnt;
int head[MAXN];
int R[MAXN],f[MAXN][2],fa[MAXN];
struct edge{
int y,next;
}e[MAXN];
void add_edge(int x, int y) {
e[++cnt].y = y;
e[cnt].next = head[x];
head[x] = cnt;
}
void dfs(int now) {
f[now][1] = R[now];
f[now][0] = 0;//初始化
for(int i = head[now]; i; i = e[i].next ) {//i是边编号
int nn = e[i].y ;
dfs(nn);//先递归进去处理儿子的f值
f[now][0] += max(f[nn][1] ,f[nn][0]) ;
f[now][1] += max(f[nn][0] , 0);
//注:因为一棵树可能有多个儿子,所以这里都是+=;
}
}
int main() {
scanf("%d",&n);
for(int i = 1; i <= n; i++) {
scanf("%d",&R[i]);
}
int l,k;
for(int i = 1; i <= n; i++) {
scanf("%d%d",&l,&k);
if(i == n) break;//只输入了n-1行
fa[l] = k;
add_edge(k,l);
}
int root;
for(int i = 1; i <= n; i++) if(!fa[i]) {
root = i;
break;//找根
}
dfs(root);
int ans = max(f[root][0],f[root][1]);
printf("%d",ans);
}
luoguP2014 选课(注意树型dp要符合dp的特点)
https://www.luogu.org/problem/P2014
注意枚举当前节点所选的课要倒序枚举,原因同上
#include<cstdio>
#include<algorithm>
using namespace std;
const int MAX = 300+9;
int n,m;
int f[MAX][MAX], arr[MAX], size[MAX];
//f[i][j]表示子树i中(包括i)选j门课的最大学分
struct edge{
int y, next;
}e[MAX<<1];
int head[MAX], cnt;
void add_edge(int x, int y) {
e[++cnt].y = y;
e[cnt].next = head[x];
head[x] = cnt;
}
void dfs(int x) {
size[x] = 1;
for(int i = head[x]; i; i = e[i].next) {
dfs(e[i].y);
size[x] += size[e[i].y];
for(int k = size[x]; k >= 1; k--) {//父亲不选就都不能选,所以>=1//这儿的k必须倒序
for(int j = 0; j < k; j++) {//枚举在当前儿子中选的课
f[x][k] = max(f[x][k],f[x][k-j] + f[e[i].y][j]);
}
}
}
}
int main() {
scanf("%d%d",&n,&m);
int x;
for(int y = 1; y <= n; y++) {
scanf("%d%d",&x,&arr[y]);
add_edge(x,y);
}
m++;
// f[0][1] = 0;
for(int i = 1; i <= n; i++) f[i][1] = arr[i];
dfs(0);
printf("%d",f[0][m]);
}
luoguP2015 二叉苹果树
注: 因为相当于0-1背包中的选或不选,所以 j 是逆序的,其他细节在代码里有体现和解释
https://www.luogu.org/problemnew/show/P2015
#include<cstdio>
#include<algorithm>
using namespace std;
const int MAXN = 100+9;
int n,Q;
int f[MAXN][MAXN];//f[i][j]表示: 点i和它的子树保留j个树枝时的最大苹果数
int head[MAXN],cnt;
struct edge{
int y,val,next;
}e[MAXN];
void add_edge(int x, int y, int val) {
e[++cnt].y = y;
e[cnt].val = val;
e[cnt].next = head[x];
head[x] = cnt;
}
void dfs(int now ,int dad) {
f[now][0] = 0;//初始化边界
for(int i = head[now]; i; i = e[i].next ) {
int nn = e[i].y ;
if(nn == dad) continue ;//应该是continue吧,不是return ;
dfs(nn, now);//先递归进去处理儿子的f值
for(int j = Q; j; j--) {//逆序的原因: 0-1背包选或不选
for(int k = 0; k < j; k++) {//枚举 左/右 子树保留的树枝
f[now][j] = max(f[now][j], f[now][j-k-1] + f[nn][k] + e[i].val );
//要选子树nn上边,就要把子树nn与根的边选上,所以这里是j-k还要"-1"
}
}
}
}
int main() {
scanf("%d%d",&n,&Q);
int m = n-1;//二叉树的边
for(int i = 1, x, y, val; i <= m; i++) {
scanf("%d%d%d",&x,&y,&val);
add_edge(x,y,val);
add_edge(y,x,val);//只是描述了边,但不知道父亲是谁,儿子是谁,所以建双向的
//所以下面的dfs要开一个树根的形参,防止死循环
}
//1为根
dfs(1,1);
printf("%d",f[1][Q]);
return 0;
}
初学树型dp的更多相关文章
- POJ3659 Cell Phone Network(树上最小支配集:树型DP)
题目求一棵树的最小支配数. 支配集,即把图的点分成两个集合,所有非支配集内的点都和支配集内的某一点相邻. 听说即使是二分图,最小支配集的求解也是还没多项式算法的.而树上求最小支配集树型DP就OK了. ...
- POJ 3342 - Party at Hali-Bula 树型DP+最优解唯一性判断
好久没写树型dp了...以前都是先找到叶子节点.用队列维护来做的...这次学着vector动态数组+DFS回朔的方法..感觉思路更加的清晰... 关于题目的第一问...能邀请到的最多人数..so ea ...
- 【XSY1905】【XSY2761】新访问计划 二分 树型DP
题目描述 给你一棵树,你要从\(1\)号点出发,经过这棵树的每条边至少一次,最后回到\(1\)号点,经过一条边要花费\(w_i\)的时间. 你还可以乘车,从一个点取另一个点,需要花费\(c\)的时间. ...
- 洛谷P3354 Riv河流 [IOI2005] 树型dp
正解:树型dp 解题报告: 传送门! 简要题意:有棵树,每个节点有个权值w,要求选k个节点,最大化∑dis*w,其中如果某个节点到根的路径上选了别的节点,dis指的是到达那个节点的距离 首先这个一看就 ...
- 【POJ 3140】 Contestants Division(树型dp)
id=3140">[POJ 3140] Contestants Division(树型dp) Time Limit: 2000MS Memory Limit: 65536K Tot ...
- Codeforces 581F Zublicanes and Mumocrates(树型DP)
题目链接 Round 322 Problem F 题意 给定一棵树,保证叶子结点个数为$2$(也就是度数为$1$的结点),现在要把所有的点染色(黑或白) 要求一半叶子结点的颜色为白,一半叶子结点的 ...
- ZOJ 3949 (17th 浙大校赛 B题,树型DP)
题目链接 The 17th Zhejiang University Programming Contest Problem B 题意 给定一棵树,现在要加一条连接$1$(根结点)和$x$的边,求加 ...
- BZOJ 1564 :[NOI2009]二叉查找树(树型DP)
二叉查找树 [题目描述] 已知一棵特殊的二叉查找树.根据定义,该二叉查找树中每个结点的数据值都比它左儿子结点的数据值大,而比它右儿子结点的数据值小. 另一方面,这棵查找树中每个结点都有一个权值,每个结 ...
- Codeforces 149D Coloring Brackets(树型DP)
题目链接 Coloring Brackets 考虑树型DP.(我参考了Q巨的代码还是略不理解……) 首先在序列的最外面加一对括号.预处理出DFS树. 每个点有9中状态.假设0位不涂色,1为涂红色,2为 ...
随机推荐
- Django2.1集成xadmin管理后台所遇到的错误集锦,解决填坑(二)
django默认是有一个admin的后台管理模块,但是丑,功能也不齐全,但是大神给我们已经集成好了xadmin后台,我们拿来用即可,但是呢,django已经升级到2.1版本了,xadmin貌似跟不上节 ...
- Impala基于内存的SQL引擎的详细介绍
一.简介 1.概述 Impala是Cloudera公司推出,提供对HDFS.Hbase数据的高性能.低延迟的交互式SQL查询功能. •基于Hive使用内存计算,兼顾数据仓库.具有实时.批处理.多并发等 ...
- requests---requests上传图片
我们在做接口测试的时候肯定会遇到一些上传图片,然后进行校验,今天我们一起学习通过requests上传图片,查看是否上传成功 抓取上传接口 这里我以百度为例子进行操作,为啥要用百度呢,主要上传文件比较简 ...
- Linux 和 Windows 查看当前运行的 python 进程及 GPU、CPU、磁盘利用率
目录 查看当前 python 进程 Linux Windows 查看 GPU 利用率 Linux Windows Linux CPU 利用率 Linux 磁盘利用率 查看当前 python 进程 Li ...
- 【oracle】表和索引建立在不用表空间原因
磁盘I/O竞争,要放在[真]的不同的磁盘上. Oracle强烈建议,任何一个应用程序的库表至少需要创建两个表空间,其中之一用于存储表数据,而另一个用于存储表索引数据.因为如果将表数据和索引数据放在一起 ...
- jenkins传统模式发布istio应用
一.发布金丝雀版本 Pre Setps cd /var/lib/jenkins/workspace/istio-service-user-canary/istio-service-user # 旧版本 ...
- Zotero入门精通
一.Zotero简介 Zotero作为一款协助科研工作者收集.管理以及引用研究资源的免费软件,如今已被广泛使用.此篇使用说明主要分享引用研究资源功能,其中研究资源可以包括期刊.书籍等各类文献和网页.图 ...
- PHP面试题大全(值得收藏)
PHP进阶.面试:文档.视频资源点击免费获取 一 .PHP基础部分 1.PHP语言的一大优势是跨平台,什么是跨平台? PHP的运行环境最优搭配为Apache+MySQL+PHP,此运行环境可以在不同操 ...
- SQL Server设置数据库为状态为只读
问题描述: 有时候我们为了防止连接数据库再链接插入数据,就可以把库设置为只读模式 灰色后面显示只读说明已经是只读状态了 1.设置为只读状态 USE [master] GO ALTER DATABASE ...
- PHPStorm设置等号对齐
为了代码的美观,我们常常会把代码等号设置对齐,手动对齐的效率很低,PHPStrom提供了快捷键来一键对齐. 首先设置PHPStorm 设置完PHPStorm后,使用快捷键Command+Option+ ...