宝藏(树形DP)
这道题目是十分考验思维的,n^2应该还是比较好想的,主要是如何转移根的问题。转移根,在我看来应该是树形dp最难的一部分了,
一般学会如何转移根,也就差不多考验通吃树形dp了。
下面转一转大佬链接:http://blog.csdn.net/FromATP/article/details/53200240
题解
比较好想的树形DP。。但是实现起来很多地方不是一般恶心。。。也许是ATP的方法太愚蠢了?总之好像当时考试的时候ATP最后半个小时想出来了但是没码完只好写了50pts部分分= =醉死了
那就先从50pts的开始说起吧。。可以想到这个数据范围允许我们把每个节点当做根每次都重新进行dfs,那就用一个f[i][0]表示从i号节点出发到它的子树里去转一圈最后还要回来的最大收益,f[i][1]表示从i号节点出发到它的子树里转一圈最后不回来的最大收益。那么分别以每个点u为根dfs的时候最大收益就是f[u][0]和f[u][1]的Max。
但是100pts的话肯定不能每次重新dfs,就要考虑一遍dfs维护出所有需要的信息。因为它要的是一定经过某一个点的最大收益路线,一遍dfs的话只能维护出从这个点出发到它的子树里走一圈的最大收益,于是再维护一个g[i][0]数组代表从i这个点出去到它的子树外面走一圈并且回来的最大收益,g[i][1]表示从这个点出去走一圈并且回来的最大收益。但是注意的情况是递推g数组的时候因为要分成在它父亲外面走一圈和在它兄弟节点里走一圈两种情况,所以搞它的兄弟节点的时候不能包括它本身。这个需要注意。
那么到底如何来维护这两个数组呢?对于f[i][0],反正它每次走出去都要回来,所以就一个子树一个子树地考虑。对于当前子树v,f[v][0]是已知的,那么跑到这个子树里面走一圈再回来的收益就可以算出来。如果这个收益大于0,显然选上v这个子树肯定会让解变得更优。也就是说f[i][0]是贪心维护的,枚举每一棵子树的时候只要它的收益大于0就一定要选。
对于f[i][1],它需要在i的所有子树中选一棵子树,当跑到这棵子树里面的时候它就不再回到点i了。除了这棵子树以外的其它子树还是可以用f[v][0]来贪心选择的。那么在递推f[i][1]的时候每次枚举到一个子树v,显然目前的f[i][1]里面存储的结果对应的那棵不回来的子树在v前面。于是就要分成仍然让v之前的那棵子树不回来和让v不回来两种情况讨论。如果仍然让v之前的那棵子树不回来,v就要回来,于是就用f[v][0]贪心地更新;否则因为v要回来,所以v之前所有的都不能回来,就用当前的f[i][0]加上f[v][1]来更新结果——这就要求每次f[i][0]要在f[i][1]之后更新否则f[i][0]里面存的就不是v之前的子树而有可能包括v了,就造成了重复更新。
g数组要求自顶向下而不是自底向上递推,因为只有知道了父亲的情况才能递推儿子。对于g[i][0],它是跑到外面转一圈再回来的最大收益,于是首先继承g[fa][0]的结果,表示它到它父亲节点的外面转了一圈再回来;然后还有它经过父亲到兄弟节点里面转一圈回来的情况,这个肯定不能枚举现算啊对吧,所以我们考虑利用之前的结果。注意到f[fa][0]就已经包括了i的所有兄弟节点的信息,但是问题就在于有可能也同时包括了i,这是不合法的。所以我们要判断一下i有没有对f[fa][0]产生贡献,如果有的话要减去。因为是贪心选择的所以也很好判断。
最后是g[i][1]。需要分成不回来的那条路在它父亲外面还是在它兄弟节点里面两部分讨论。如果不回来的那条路在父亲外面的话就是用g[fa][1]加上它到兄弟节点里转一圈然后回来的收益,这个和上面g[i][0]计算的时候是一样的。关键就是不回来的那条路再兄弟节点里面的这种情况比较麻烦。不能直接用f[fa][1]啥的来更新,因为在计算f[fa][1]的时候所使用的不回来的那个儿子可能本身就是当前要计算的i。也就是用于递推i的那个东西必须保证它不回来的那个儿子必须在i外面。所以在递推f数组的时候又循环了一遍,一开始递推f[u][1]的时候记录了一下它不回来的儿子是哪一个,然后再强制让这个儿子一定拐回来,递推一个f[u][2]。这样在递推g[i][1]的时候,f[fa][1]和f[fa][2]一定至少有一个它选择的不回来的路径是在i外面的,就用那个来更新。更新的时候仍然记得减去可能存在的f[i][0]的贡献。
虽然十分长,但是十分详细,十分~\(≧▽≦)/~。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,p[],a[],nxt[],tot;
long long f[][],g[][],val[],w[],rec[];
void add(int x,int y,long long v){
tot++;a[tot]=y;nxt[tot]=p[x];w[tot]=v;p[x]=tot;
}
void dfs(int u,int fa){
int tmp=val[u],e;
f[u][]=f[u][]=f[u][]=val[u];//0:back;1:not back
for (int i=p[u];i!=;i=nxt[i])
if (a[i]!=fa){
int now,last;
dfs(a[i],u);
last=f[u][]+max((long long),f[a[i]][]-*w[i]);
now=f[u][]+max((long long),f[a[i]][]-w[i]);
if (now>last){
f[u][]=now;rec[u]=a[i];
}else f[u][]=last;
if (f[a[i]][]>=*w[i]) f[u][]+=f[a[i]][]-*w[i];
}
for (int i=p[u];i!=;i=nxt[i])
if (a[i]!=fa&&a[i]!=rec[u]){
int now,last;
last=f[u][]+max((long long),f[a[i]][]-*w[i]);
now=tmp+max((long long),f[a[i]][]-w[i]);
f[u][]=max(now,last);
if (f[a[i]][]>=*w[i]) tmp+=f[a[i]][]-*w[i];
}else if(a[i]==rec[u]) e=w[i];//记下最大值不回来节点的父边来更新
f[u][]=max(f[u][],f[u][]+f[rec[u]][]-*e);//最后要判断一下
}
void dfs_again(int u,int fa,int edge){
int son;
g[u][]=g[u][]=;
if (rec[fa]==u) son=f[fa][];
else son=f[fa][];//判断选择最大值还是次大值
son-=max(f[u][]-*edge,(long long));//减去当前子树的影响
g[u][]=max(g[u][],g[fa][]+son-edge);//停在兄弟节点里的情况
if (f[u][]>*edge) son=f[u][]-*edge;
else son=;//计算所有兄弟节点回来的价值和
son=f[fa][]-son;//停在父亲外面的情况
g[u][]=max(g[u][],son+g[fa][]-edge);
g[u][]=max(g[u][],son+g[fa][]-*edge);
for (int i=p[u];i!=;i=nxt[i])
if (a[i]!=fa) dfs_again(a[i],u,w[i]);
}
int main()
{
freopen("treasure.in","r",stdin);
freopen("treasure.out","w",stdout);
scanf("%d",&n);
for (int i=;i<n;i++){
int x,y,w;
scanf("%d%d%d",&x,&y,&w);
add(x,y,w);add(y,x,w);
}
for (int i=;i<=n;i++) scanf("%d",&val[i]);
dfs(,);dfs_again(,,);
for (int i=;i<=n;i++)
printf("%I64d\n",max(f[i][]+g[i][],f[i][]+g[i][]));
return ;
}
附上了他的代码,我的代码在模拟赛中有写到。
宝藏(树形DP)的更多相关文章
- HDU 1561 The more, The Better【树形DP/有依赖的分组背包】
ACboy很喜欢玩一种战略游戏,在一个地图上,有N座城堡,每座城堡都有一定的宝物,在每次游戏中ACboy允许攻克M个城堡并获得里面的宝物.但由于地理位置原因,有些城堡不能直接攻克,要攻克这些城堡必须先 ...
- codevs1486愚蠢的矿工(树形dp)
1486 愚蠢的矿工 时间限制: 1 s 空间限制: 128000 KB 题目描述 Description Stupid 家族得知在HYC家的后花园里的中央花坛处,向北走3步,向西走3步, ...
- poj3417 LCA + 树形dp
Network Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 4478 Accepted: 1292 Descripti ...
- COGS 2532. [HZOI 2016]树之美 树形dp
可以发现这道题的数据范围有些奇怪,为毛n辣么大,而k只有10 我们从树形dp的角度来考虑这个问题. 如果我们设f[x][k]表示与x距离为k的点的数量,那么我们可以O(1)回答一个询问 可是这样的话d ...
- 【BZOJ-4726】Sabota? 树形DP
4726: [POI2017]Sabota? Time Limit: 20 Sec Memory Limit: 128 MBSec Special JudgeSubmit: 128 Solved ...
- 树形DP+DFS序+树状数组 HDOJ 5293 Tree chain problem(树链问题)
题目链接 题意: 有n个点的一棵树.其中树上有m条已知的链,每条链有一个权值.从中选出任意个不相交的链使得链的权值和最大. 思路: 树形DP.设dp[i]表示i的子树下的最优权值和,sum[i]表示不 ...
- 树形DP
切题ing!!!!! HDU 2196 Anniversary party 经典树形DP,以前写的太搓了,终于学会简单写法了.... #include <iostream> #inclu ...
- BZOJ 2286 消耗战 (虚树+树形DP)
给出一个n节点的无向树,每条边都有一个边权,给出m个询问,每个询问询问ki个点,问切掉一些边后使得这些顶点无法与顶点1连接.最少的边权和是多少.(n<=250000,sigma(ki)<= ...
- POJ2342 树形dp
原题:http://poj.org/problem?id=2342 树形dp入门题. 我们让dp[i][0]表示第i个人不去,dp[i][1]表示第i个人去 ,根据题意我们可以很容易的得到如下递推公式 ...
随机推荐
- css基础:样式属性
1.背景与前景:background-color::背景色,样式表优先级高. background-image:url(路径):设置背景图片 background-attachment:fixed:背 ...
- RPC框架实现思路浅析
第一部分,设计分析 远程调用要解决的主要问题: 1,序列化 : 如何将对象转化为二进制数据进行传输,如何将二进制数据转化对象 2,数据的传输(协议,第三方框架) 3,服务的注册/发现,单点故障,分布式 ...
- JS判断当前使用设备是pc端还是web端(转MirageFireFox)
js判断当前设备 最近用bootstrap做自适应,发现仍然很难很好的兼容web端和PC端的现实. 仔细观察百度,淘宝,京东等大型网站,发现这些网站都有对应不同客户端的子站. 站点 PC端url we ...
- ElasticSearch和Kibana 5.X集群的安装
ElasticSearch和Kibana 5.X集群的安装 1.准备工作 1.1.下载安装包 1.2.系统的准备 2.ElasticSearch集群的安装 2.1.修改 config/elastics ...
- Spring Boot-------项目搭建及注解
Spring Boot Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程.该框架使用了特定的方式来进行配置,从而使开发人员不再需 ...
- 汇编指令-位置无关码(BL)与绝对位置码(LDR)(2)
位置无关码,即该段代码无论放在内存的哪个地址,都能正确运行.究其原因,是因为代码里没有使用绝对地址,都是相对地址. 位置相关码,即它的地址与代码处于的位置相关,是绝对地址 BL :带链接分支跳转指令 ...
- [自制操作系统] 图形界面&VBE工具&MMIO显存&图形库/字库
本文记录了在JOS(或在任意OS)上实现图形界面的方法与一些图形库的实现. 本文中支持的新特性: 支持基本图形显示 支持中英文显示(中英文点阵字库) 相关:VBE VESA MMIO 点阵字库 Git ...
- 【深入Java虚拟机】之七:Javac编译与JIT编译
转载请注明出处:http://blog.csdn.net/ns_code/article/details/18009455 编译过程 不论是物理机还是虚拟机,大部分的程序代码从开始编译到最终转化成物理 ...
- 201521123089 《Java程序设计》第8周学习总结
1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结集合与泛型相关内容. 1.2 选做:收集你认为有用的代码片段 2. 书面作业 本次作业题集集合 1.List中指定元素的删除(题目4 ...
- Servlet的生命周期与运行原理
Servlet的生命周期: 1 加载classLoader 2 实例化 new 3 初始化 init(ServletConfig) 4 处理请求 service doGet d ...