博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
洛谷p1122 Tree DP
阅读量:5283 次
发布时间:2019-06-14

本文共 1542 字,大约阅读时间需要 5 分钟。

题意:给定一颗无根树,每个点有一个点权,可以有负数。求一个联通块,使得这个联通块内的点权之和最大。

分析:我们看到这道题,第一反应是贪心。把每个点当做根跑一次贪心,在根的子树中,如果一个子树的权值和大于0,就把子树的权值加上,否则就剪去这个子树。这样从根节点一层一层递归下来,先处理点的子树,再处理点本身,我们就得到了以某个节点为根的最大权值和。每个点都跑一次,取一个最大值,就得到了答案。这样做一定是对的,但是复杂度O(n^2),加一个记忆化搜索能过。

  但今天要讲的是Tree DP的思路。随便选一个点作为根,记f[i]为以i为根的子树的最大权值和,跑一次树上的DP就行。虽然有递归,但每个点只会经过一次,复杂度O(n)。

至于为什么以任意一个点作为根都行,请看下面的分析:
假设我们以节点1为根算出了一个最大联通块和,此时的联通块中一定包含节点1的权值。如果以2为根算最大权值,那分为两种情况:如果没有节点2这一部分,f[1]都大于0的话,那么节点2做DP时一定会把1那部分加上,结果就等效为从节点1做DP。如果没有节点2这一部分,f[1]小于0的话,说明节点1的权值小于0,那么2肯定不会选1这部分,程序中很重要的一句ans=max(ans,f[u]);就发挥了作用,它记录了以2为根的子树的答案,相当于以2为根不选1那部分的答案,还是等效的。所以,以2为根的话,无论哪种情况,都是和以1为根的情况是等效的,其他点同理。所以,我们只要任选一个点为根就可以了。

 

#include
using namespace std;int n,ans,ecnt;int a[16005];bool vis[16005];int head[16005];int f[16005];struct aaa{ int to,nxt;}bian[32100];inline int read(){ int x=0;bool f=0;char c=getchar(); while(c<'0'||c>'9'){
if(c=='-')f=1;c=getchar();} while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+c-'0';c=getchar();} return f?-x:x;}inline void add(int a,int b){ bian[++ecnt].to=b; bian[ecnt].nxt=head[a]; head[a]=ecnt;}void dfs(int u){ vis[u]=1;f[u]=a[u]; for(int i=head[u];i;i=bian[i].nxt) { int v=bian[i].to; if(!vis[v]) { dfs(v); f[u]+=max(0,f[v]); } } ans=max(ans,f[u]);}int main(){ n=read(); int aa,bb; for(int i=1;i<=n;i++)a[i]=read(); for(int i=1;i<=n-1;i++) { aa=read();bb=read(); add(aa,bb);add(bb,aa); } dfs(1); cout<

 

转载于:https://www.cnblogs.com/oierjzy/p/11260140.html

你可能感兴趣的文章
实训第五天
查看>>
平台维护流程
查看>>
2012暑期川西旅游之总结
查看>>
12010 解密QQ号(队列)
查看>>
2014年辛星完全解读Javascript第一节
查看>>
装配SpringBean(一)--依赖注入
查看>>
java选择文件时提供图像缩略图[转]
查看>>
方维分享系统二次开发, 给评论、主题、回复、活动 加审核的功能
查看>>
Matlab parfor-loop并行运算
查看>>
string与stringbuilder的区别
查看>>
2012-01-12 16:01 hibernate注解以及简单实例
查看>>
iOS8统一的系统提示控件——UIAlertController
查看>>
PAT甲级——1101 Quick Sort (快速排序)
查看>>
python创建进程的两种方式
查看>>
1.2 基础知识——关于猪皮(GP,Generic Practice)
查看>>
迭代器Iterator
查看>>
java易错题----静态方法的调用
查看>>
php建立MySQL数据表
查看>>
最简单的线程同步的例子
查看>>
旅途上看的电影和观后感
查看>>