宝藏(树形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个人去 ,根据题意我们可以很容易的得到如下递推公式 ...
随机推荐
- 处理 Vue 单页面应用 SEO 的另一种思路
vue-meta-info 官方地址: monkeyWangs/vue-meta-info (设置vue 单页面meta info信息,如果需要单页面SEO,可以和 prerender-spa-plu ...
- 快速排序Java实现
package practice; import edu.princeton.cs.algs4.*; public class TestMain { public static void main(S ...
- 持续交付Jenkins使用
简介 Jenkins是一个独立的开源自动化服务器,可用于自动化各种任务,如构建,测试和部署软件.Jenkins可以通过本机系统包Docker安装,甚至可以通过安装Java Runtime Enviro ...
- CVE-2016-10190 FFmpeg Http协议 heap buffer overflow漏洞分析及利用
作者:栈长@蚂蚁金服巴斯光年安全实验室 -------- 1. 背景 FFmpeg是一个著名的处理音视频的开源项目,非常多的播放器.转码器以及视频网站都用到了FFmpeg作为内核或者是处理流媒体的工具 ...
- poj 2455 二分+最大流
这个因为点少用邻接矩阵做的. 题意:求由1到n的t条不重复路径中最大边权值的最小值. 思路:先对边权进行排序,然后二分边权值,建图求从1到n的最大流,当最大流为t时便求出答案. 代码: #includ ...
- 最近一直在做java爬虫,有些感悟心得,分享给大家;
首先,看完这篇文章,不能保证你成为大神,但是却可以让你懂得什么是爬虫,如何使用爬虫,如何利用http协议,侵入别人的系统,当然只是一些简单的教程,拿到一些简单的数据: 先上代码,在一步一步讲解: 这是 ...
- iOS ipa包瘦身------删除无用图片资源
随着客户端业务的增多和业务的更新,App包大小越来越大,优化包大小是迫在眉睫,客户端需要优化的地方也有很多,本期主要讲如何查找无用图片并且删除无用图片的方法. 方案1:(暴力方法) ...
- Mac上好用的视频播放器有哪些?
首页发现话题 提问 登录加入知乎 Mac 上好用的视频播放器有哪些? 关注问题写回答 OS X 应用 多媒体播放器(软件) Mac 上好用的视频播放器有哪些? 关注者 2680 被浏览 981770 ...
- 转:JDK中的URLConnection参数详解
针对JDK中的URLConnection连接Servlet的问题,网上有虽然有所涉及,但是只是说明了某一个或几个问题,是以FAQ的方式来解决的,而且比较零散,现在对这个类的使用就本人在项目中的使用经验 ...
- springmvc注解
简介: handler method 参数绑定常用的注解,我们根据他们处理的Request的不同内容部分分为四类:(主要讲解常用类型) A.处理requet uri 部分(这里指uri templat ...