树形DP 学习笔记(树形DP、树的直径、树的重心)
前言:寒假讲过树形DP,这次再复习一下。
--------------
基本的树形DP
实现形式
树形DP的主要实现形式是$dfs$。这是因为树的特殊结构决定的——只有确定了儿子,才能决定父亲。划分阶段的话一般是$f[i][j][0/1]$。$i$表示以$i$为根的子树,$j$一般表示保留$j$个子节点,$0/1$表示选/不选这个节点。一般第三维可以省去。
基本的DP方程
选择节点类
$f[i][0]=f[j][1]$
$f[i][1]=max/min(f[j][0],f[j][1])$
背包类
$f[v][k]=f[u][k]+val$
$f[u][k]=max(f[u][k],f[v][k-1])$
例题
没有上司的舞会
设$f[i][0/1]$表示$i$这个人去/不去舞会能获得的最大快乐指数。
状态转移方程:
$f[u][0]=max(f[v][0],f[v][1])$
$f[u][1]=val[u]+f[v][0]$
最大子树和
设$f[i]$表示以$i$为根的子树所能获得的最大美丽指数。
状态转移方程:$f[u]=val[u]+max(f[v],0)$
注意有负数。
树形DP求树的直径
树的直径,即树上最远点对,一般有两种做法:两次$dfs$和树形$DP$。两种方法这里都会讲一下。
两次$dfs$
步骤:我们先找任意一点的最远点$p$,再找$p$的最远点$w$。从$p$到$w$的距离就是树的直径。
证明:
①若$P$已经在直径上,根据树的直径的定义可知$Q$也在直径上且为直径的一个端点
②若$P$不在直径上,我们用反证法,假设此时$WQ$不是直径,$AB$是直径
--->若$AB$与$PQ$有交点$C$,由于$P$到$Q$最远,那么$PC+CQ>PC+CA$,所以$CQ>CA$,易得$CQ+CB>CA+CB$,即$CQ+CB>AB$,与$AB$是直径矛盾,不成立。
--->若$AB$与$PQ$没有交点,$M$为$AB$上任意一点,$N$为$PQ$上任意一点。首先还是$NP+NQ>NQ+MN+MB$,同时减掉$NQ$,得$NP>MN+MB$,易知$NP+MN>MB$,所以$NP+MN+MA>MB+MA$,即$NP+MN+MA>AB$,与$AB$是直径矛盾,所以这种情况也不成立。
代码:
//
#include<bits/stdc++.h>
using namespace std;
int dis[],ans,p;
struct node
{
int next,to,dis;
}edge[];
int head[],cnt;
inline void add(int from,int to,int dis)
{
edge[++cnt].next=head[from];
edge[cnt].to=to;
edge[cnt].dis=dis;
head[from]=cnt;
}
void dfs(int now,int fa)
{
if (ans<dis[now])
{
ans=dis[now];p=now;
}
for (int i=head[now];i;i=edge[i].next)
{
int to=edge[i].to;
if (to==fa) continue;
dis[to]=dis[now]+edge[i].dis;
dfs(to,now);
}
}
void find(int x)
{
ans=;
dis[x]=;
dfs(x,);
}
int main()
{
cin>>n>>m;
for (int i=;i<=m;i++)
{
int u,v,d;
cin>>u>>v>>d;
add(u,v,d);add(v,u,d);
}
find();
find(p);
cout<<ans;
return ;
}
树形DP
设$dis1[i]$和$dis2[i]$表示以$i$为根的子树中以$i$为起点的最大距离和次大距离。
状态转移方程:
$if (dis1[i]<dis1[j]+edge[i].dis) dis2[i]=dis1[i],dis1[i]=dis1[j]+edge[i].dis$
$else if (dis2[i]<dis1[j]+edge[i].dis) dis2[i]=dis1[j]+edge[i].dis$。
理解:这样做就是,先看能否更新最大值,若能,它的次大值就是原先的最大值,再更新它的最大值;若不能,就看能不能更新次大值,若能,就更新,不能就不管它。
$ans=\max {dis1[i]+dis2[i]}$
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=;
int n,m,t,ans;
int f1[N],f2[N];
int first[N],v[N],w[N],next[N];
void add(int x,int y,int z){
t++;
next[t]=first[x];
first[x]=t;
v[t]=y;
w[t]=z;
}
void dp(int x,int father)
{
int i,j;
for(i=first[x];i;i=next[i])
{
j=v[i];
if(j==father) continue;
dp(j,x);
if(f1[x]<f1[j]+w[i])
{
f2[x]=f1[x];
f1[x]=f1[j]+w[i];
}
else if(f2[x]<f1[j]+w[i])
f2[x]=f1[j]+w[i];
ans=max(ans,f1[x]+f2[x]);
}
}
int main()
{
int x,y,z,i;
scanf("%d%d",&n,&m);
for(i=;i<=m;++i)
{
scanf("%d%d%d",&x,&y,&z);
add(x,y,z);
add(y,x,z);
}
dp(,);
printf("%d",ans);
return ;
}
树形DP求树的重心
对于一棵$n$个节点的无根树,找到一个点,使得把树变成以该点为根的有根树时,最大子树的节点数最小。换句话说,删除这个点后最大连通块的节点数最小,那么这个点就是树的重心。
解法:任选一个节点为根,把无根树变成有根树,然后设$f[i]$表示以$i$为根的子树的节点个数。不难发现$f[i]=\sum \limits_{j\in s[i]}f[j]+1$。程序实现很简单:只需要一次$dfs$,在无根树转化成有根树的同时计算即可。其实在删除节点$i$后,最大的连通块有多少个节点呢?节点$i$的子树中最大的有$max{f[j]}$个节点,$i$的上方子树中有$n-f[i]$个节点,在$DP$过程中根据定义就可以找出树的重心了。
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <vector>
using namespace std; vector<int> G[];
int dp[],tot[];
vector<int> ans; void dfs(int u)
{
tot[u] = ;
for(int i=;i<G[u].size();i++)
{
int v = G[u][i];
if(tot[v] == -)
dfs(v);
else
continue;
dp[u] = max(dp[u],tot[v]);
tot[u] += tot[v];
}
} int main()
{
int n,i,j,u,v;
scanf("%d",&n);
for(i=;i<=n;i++)
G[i].clear();
for(i=;i<n-;i++)
{
scanf("%d%d",&u,&v);
G[u].push_back(v);
G[v].push_back(u);
}
memset(dp,,sizeof(dp));
memset(tot,-,sizeof(tot));
dfs();
int mini = Mod;
for(i=;i<=n;i++)
{
int tmp = max(dp[i],n-tot[i]);
if(tmp < mini)
{
ans.clear();
ans.push_back(i);
mini = tmp;
}
else if(tmp == mini)
ans.push_back(i);
}
printf("%d %d\n",mini,ans.size());
sort(ans.begin(),ans.end());
printf("%d",ans[]);
for(i=;i<ans.size();i++)
printf(" %d",ans[i]);
puts("");
return ;
}
树形DP 学习笔记(树形DP、树的直径、树的重心)的更多相关文章
- 树形DP 学习笔记
树形DP学习笔记 ps: 本文内容与蓝书一致 树的重心 概念: 一颗树中的一个节点其最大子树的节点树最小 解法:对与每个节点求他儿子的\(size\) ,上方子树的节点个数为\(n-size_u\) ...
- 数位DP学习笔记
数位DP学习笔记 什么是数位DP? 数位DP比较经典的题目是在数字Li和Ri之间求有多少个满足X性质的数,显然对于所有的题目都可以这样得到一些暴力的分数 我们称之为朴素算法: for(int i=l_ ...
- DP学习笔记
DP学习笔记 可是记下来有什么用呢?我又不会 笨蛋你以后就会了 完全背包问题 先理解初始的DP方程: void solve() { for(int i=0;i<;i++) for(int j=0 ...
- 树形$dp$学习笔记
今天学习了树形\(dp\),一开始浏览各大\(blog\),发现都\(TM\)是题,连个入门的\(blog\)都没有,体验极差.所以我立志要写一篇可以让初学树形\(dp\)的童鞋快速入门. 树形\(d ...
- [学习笔记]树形dp
最近几天学了一下树形\(dp\) 其实早就学过了 来提高一下打开树形\(dp\)的姿势. 1.没有上司的晚会 我的人生第一道树形\(dp\),其实就是两种情况: \(dp[i][1]\)表示第i个人来 ...
- [总结] 动态DP学习笔记
学习了一下动态DP 问题的来源: 给定一棵 \(n\) 个节点的树,点有点权,有 \(m\) 次修改单点点权的操作,回答每次操作之后的最大带权独立集大小. 首先一个显然的 \(O(nm)\) 的做法就 ...
- 洛谷4719 【模板】动态dp 学习笔记(ddp 动态dp)
qwq大概是混乱的一个题. 首先,还是从一个比较基础的想法开始想起. 如果每次暴力修改的话,那么每次就可以暴力树形dp 令\(dp[x][0/1]\)表示\(x\)的子树中,是否选择\(x\)这个点的 ...
- 动态 DP 学习笔记
不得不承认,去年提高组 D2T3 对动态 DP 起到了良好的普及效果. 动态 DP 主要用于解决一类问题.这类问题一般原本都是较为简单的树上 DP 问题,但是被套上了丧心病狂的修改点权的操作.举个例子 ...
- 斜率优化DP学习笔记
先摆上学习的文章: orzzz:斜率优化dp学习 Accept:斜率优化DP 感谢dalao们的讲解,还是十分清晰的 斜率优化$DP$的本质是,通过转移的一些性质,避免枚举地得到最优转移 经典题:HD ...
随机推荐
- Dynamics CRM Performance Issue when CRM Forms Opening
事情发生在Dynamics CRM 8.2.2版本,客户新升级到这个版本几个月的时间. 突然有一天,客户反映为什么我们打开CRM Form页面的时候loading的时间这么长呢?大概会需要5-15分钟 ...
- SpringBoot系列之前后端接口安全技术JWT
@ 目录 1. 什么是JWT? 2. JWT令牌结构怎么样? 2.1 标头(Header) 2.2 有效载荷(Playload) 2.3 签名(Signature) 3. JWT原理简单介绍 4. J ...
- Mysql基础(十):MYSQL中使用事务的案例
https://www.cnblogs.com/lsqbk/p/10145306.html 基本介绍 事务用于保证数据的一致性,它由一组相关的dml语句组成,该组的dml语句要么全部成功,要么全部失败 ...
- java 面向对象(七):类结构 方法(四)递归方法
1.定义:递归方法:一个方法体内调用它自身.2.如何理解递归方法?> 方法递归包含了一种隐式的循环,它会重复执行某段代码,但这种重复执行无须循环控制.> 递归一定要向已知方向递归,否则这种 ...
- 数据可视化基础专题(十四):pyecharts 基础(一)简单上手
1.引言 文档位于 https://pyecharts.org/#/zh-cn/intro 示例位于 https://gallery.pyecharts.org/#/README echarts 官网 ...
- Disease Manangement 疾病管理
题目描述 Alas! \(A\) set of \(D (1 <= D <= 15)\) diseases (numbered \(1..D\)) is rshning through t ...
- Windows File Recovery - 微软官方文件恢复工具
假如你不小心误删除了文件或因各种意外情况丢失数据后,你可以通过 微软这款工具 这个工具来尝试恢复它们.WinFR 工具支持读取本机硬盘.移动硬盘.U 盘,或者连接相机.手机.使用读卡器来恢复 SD.T ...
- python多进程之multiprocessing
什么是多进程? 简单的理解:单板上运行的一个程序就是一个进程.进程是操作系统分配资源的最小单位,不同的进程之间资源不共享,进程间通信需要使用特定的方式.python提供了自带的multiprocess ...
- 请解释ASP.NET中的web页面与其隐藏类之间的关系
其实页面与其隐藏类之间就是一个部分类的关系,你在页面上放一个一个的控件就是在这个类中定义一个一个的属性, 因为是同一个类的部分类的关系,所以隐藏类可以访问到页面上控件,这样做是为了把展现与处理逻辑分开 ...
- springboot 跨域设置
/** * Configuration cors */ @Configuration public class MyConfiguration { @Bean public FilterRegistr ...