树形DP
来源:互联网 发布:永恒之柱 知乎 编辑:程序博客网 时间:2024/05/16 10:00
DP有些求骗的东西,比如树形DP。
怎么说呢,树也是个DAG,按理说,是DAG就能动规。
- 用题说事
- 先看一道题
- 再来一道题
- 给你三个题
- 几个知识点待补充
- 树的最大独立集
- 树的重心质心
- 树的直径树的最长路径最远点对
- OBST最优排序二叉树
- 总结一下
用题说事
先看一道题
洛谷P1271 聚会的快乐
得自己建树,人名字符串还要映射……板恶心……
说正事。
这道题里,上司—下属关系就是一个天然的有向边,利用它,从老板开始,往下深搜就好啦~
建树当然是好用的链式前向星啦~不过转二叉树也可以
真正写代码时,是看取不取他的下属
一个下属取,一个下属不取……
在加上本身取不取就是这一棵子树最大值。
代码 75分
再来一道题
洛谷P1271 有线电视网
分析下次上
代码 AC
给你三个题
似乎不止三个……
洛谷 P2014 选课
洛谷 P2015 二叉苹果树 (紫书p282)
UVa 12186 Another Crisis
UVa 1220 Party at Hali-Bula
UVa 1218 Perfect Service
题解会有的……
几个知识点(待补充)
来自紫书、蓝书、挑战程序设计竞赛
树的最大独立集
对于一棵 n 个结点的无根树,选出尽量多的结点,使其中任意两个结点均不相邻(即最大独立集)。
洛谷P1271就是一个最大独立集问题,不过带了权(幽默系数)。然而,P1271是有根的(上司)。
那么,对于最大独立集问题,只需随便“提溜”一个结点“当”根,然后就很开心了。
(紫书p281)
对结点i,只有两种决策:选和不选。即方程:
d(i)表示以 i 为根结点的子树的最大独立集
写代码时,方法一是记搜(如P1271);方法二是“刷表法”:对结点 i ,计算出d(i)后,更新 i 的父亲和祖父的累加值,这样只需存每个结点的父亲就好(只开一个数组)。
法一代码
int dp[N][2]; int dfs(int i,int qu){ if(dp[i][qu])return dp[i][qu]; int ans=0; for(int e=head[i];e;e=nex[e]){ int son=dfs(to[e],0); if(!qu)son=max(son,dfs(to[e],1)); ans+=son; } return dp[i][qu]=ans+son*qu; }
法二代码
树的重心(质心)
紫书p281
对于一棵 n 个结点的无根树(又是无根树),找到一个点,使把树变成以它为根的有根树时,最大子树的节点数最小。也就是说,删除它后最大的树的节点数最少。
同样,先“提溜”一个结点“当”根,设d(i)表示以 i 为根的子树中结点个数,则有:
然后随便dfs就行了。
这只是把d(i)求出来了,删除 i 时,在
中,取一个最小的f(i),此时 i 就是重心。
(紫书p281)
f(i)表示删除 i 后,最大子树的结点数。n-d(i)表示 i 的父结点连的结点总数。因为
代码
int f[N],d[N];int dfs(int i,int fa){ int sum=0; for(int e=head[i];e;e=nex[e]) if(to[e]!=fa)sum+=dfs(to[e],i); return d[i]=sum+1; }int zhongxin(int root){ int a=dfs(root,-1),ans=-1,ff=INF; for(int i=1;i<=n;i++){ int son=n-d[i]; for(int e=head[i];e;e=nex[e]) son=max(son,d[to[e]]); if(son<ff){ ff=son; ans=i; } } return ans; }
树的直径(树的最长路径、最远点对)
紫书p281、挑战程序设计竞赛p295
对 n 个结点的无根树,找到一条最长路径,即找到两个点,使它们距离最远。
普通算法就是随便提溜一个根root,然后dfs找到根最远的点,再从这个点出发,dfs找最远的点。这样,第二次dfs找的路径就是树的直径。
一眼看就是对的。可为什么对,不说了,不重要。代码很简单,不打了,挑战程序设计竞赛p295有。
重要的是DP算法。
(紫书p281)
dp只能求最长路长度,如果只要求长度,那就开心了。
设d(i)表示以 i 为根的子树中根到叶子的最大距离,则有:
其中”1”就是 i 到 j 的路径长度,也可换成权值。然后就好理解了。
最后结果就是root的子节点中,d最大的两个d(u),d(v),再加上2。(d(u)+d(i)+2)。
看上面的图:d(u),d(v)已知,u到灰色节点距离为1,v到灰色节点距离也为1,加起来就是d(u)+d(i)+2(也可带权)。
int d[N];int dfs(int i,int fa){ int ans=0; for(int e=head[u];e;e=nex[e]) if(to[e]!=fa) ans+=dfs(to[e],i); return d[i]=ans+1; }
OBST(最优排序二叉树)
蓝书p63
有 n 个符号,每个符号有一个检索频率,互不相同。将它们建成一棵排序二叉树,使总检索次数(所有符号的频率与其深度乘积之和,根的深度==1)最小。
UVa 10304 Optimal Binary Search Tree
这算序列型DP还是树状DP呢?不管,反正这个挺难的。
前方高能!!!
和XXX一样,先选根,再递归左右子树。
根的频率知道了,那么,它的儿子就要乘2,孙子就要乘3……太麻烦!直接所有的都加一次得了。子树?子树再当成根,递归。那方程就是:
d(i,j)表示序列i~j的总检索次数,sum(i,j)表示i~j频率和。
int sum[N],d[N][N];int build(int l,int r){ if(l==r)return sum[l]-sum[l-1]; if(d[l][r])return d[l][r]; int son=0; for(int k=l;k<=r;k++) son=max(son,build(l,k-1)+build(k+1,r)); return son+(sum[r]-sum[l-1]); }int work(){ for(int i=1;i<=n;i++) sum[i]+=sum[i-1];//直接在输入数组上做前缀和 return build(1,n); }
这只是比较朴素的方法……
前方真正高能!!!
能降至
证明要四边形不等式!!!
如果对于任意的a1≤a2< b1≤b2,有不等式
由上面所设,i≤j,所以套不等式:
i≤j,而i+1就不一定了,于是K(i+1,j)无意义,扔掉。
就是:
所以,枚举k就可改成K(i,j-1)~K(i+1,j)。这两个是早就算过的,从而降至
总结一下
- 树形dp
- 树形DP
- 树形dp
- 树形DP
- 树形dp
- 树形DP
- 树形DP
- 树形DP
- 树形DP
- 树形dp
- 树形dp
- 树形dp
- 树形dp
- 树形DP
- 树形DP
- 树形DP
- 树形DP
- 树形dp
- Linux VIM编辑器常用命令
- navicat 连接不上数据库(一台机器 访问 另外一台机器)
- NoSuchMethodError aop.support.AopUtils.selectInvocableMethod(Ljava/lang/reflect/Method;Ljava/lang/Cl
- JavaScript学习-Function类型
- POJ-1502 MPI Maelstrom
- 树形DP
- Android--文件压缩及文件解压
- 微信小程序开发系列(一) ——怎样开始进入小程序开发
- leetcode 433. Minimum Genetic Mutation 和Word Ladder一样BFS + 反向链表 + DFS
- 欢迎使用CSDN-markdown编辑器
- 水冷或风冷的数据中心?
- 架构模式 MVC、MVP、MVVM 的区别与联系
- Greenplum 清理垃圾、修改存储模式(行列变换) 平滑方法
- pcapy安装(windows)