宝藏(树形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个人去 ,根据题意我们可以很容易的得到如下递推公式 ...
随机推荐
- NCS8801S芯片RGB/LVDS转EDP功能简介
NCS8801S RGB/LVDS-to-eDP Converter (1/2/4-lane eDP) Features --Embedded-DisplayPort (eDP) Output 1/2 ...
- 推荐一款接口文档在线管理系统-MinDoc
项目简介 MinDoc 是一款针对IT团队开发的简单好用的文档管理系统. MinDoc 的前身是 SmartWiki 文档系统.SmartWiki 是基于 PHP 框架 laravel 开发的一款文档 ...
- 高性能消息队列 CKafka 核心原理介绍(上)
欢迎大家前往腾讯云技术社区,获取更多腾讯海量技术实践干货哦~ 作者:闫燕飞 1.背景 Ckafka是基础架构部开发的高性能.高可用消息中间件,其主要用于消息传输.网站活动追踪.运营监控.日志聚合.流式 ...
- 物联网socket通讯设备android
http://cache.baiducontent.com/c?m=9d78d513d99c16ee19bec1291a17a7384215c634608090027ea48439e573284b50 ...
- 协处理器CP15介绍—MCR/MRC指令(6)
概述:在基于ARM的嵌入式应用系统中,存储系统的操作通常是由协处理器CP15完成的.CP15包含16个32位的寄存器,其编号为0-15. 而访问CP15寄存器的指令主要是MCR和MRC这两个指令. 例 ...
- Adobe Flash Player 因过期而遭遇阻止怎么办
百度搜索"adobe flash player ppapi "并找到搜索结果中包含www.adobe.com的网址进行在线下载安装即可搞定这个问题[注意要对应你电脑系统中的浏览器, ...
- js 实现 input type="file" 文件上传示例代码
在开发中,文件上传必不可少但是它长得又丑.浏览的字样不能换,一般会让其隐藏点其他的标签(图片等)来时实现选择文件上传功能 在开发中,文件上传必不可少,<input type="file ...
- Spring Ioc DI 原理
IOC(DI):其实这个Spring架构核心的概念没有这么复杂,更不像有些书上描述的那样晦涩.Java程序员都知道:java程序中的每个业务逻辑至少需要两个或以上的对象来协作完成,通常,每个对象在使用 ...
- 201521123077 《Java程序设计》第3周学习总结
1. 本周学习总结 2. 书面作业 Q1.代码阅读 public class Test1 { private int i = 1;//这行不能修改 private static int j = 2; ...
- 戴建钊 201521123023《Java程序设计》第1周学习总结
1. 本周学习总结 Java 不同于C语言面向过程的编程思想,它是一种面向对象的编程方式,其在语句语法上与C++类似,因此刚开始可能比较容易理解 2. 书面作业 为什么java程序可以跨平台运行?执行 ...