ZOJ 3201解题报告
来源:互联网 发布:淘宝ceo 编辑:程序博客网 时间:2024/05/23 13:42
You're given a tree with weights of each node, you need to find the maximum subtree of specified size of this tree.
Tree Definition
A tree is a connected graph which contains no cycles.
Input
There are several test cases in the input.
The first line of each case are two integers N(1 <= N <= 100), K(1 <= K <= N), where N is the number of nodes of this tree, and K is the subtree's size, followed by a line with N nonnegative integers, where the k-th integer indicates the weight of k-th node. The following N - 1 lines describe the tree, each line are two integers which means there is an edge between these two nodes. All indices above are zero-base and it is guaranteed that the description of the tree is correct.
Output
One line with a single integer for each case, which is the total weights of the maximum subtree.
Sample Input
3 110 20 300 10 23 210 20 300 10 2
Sample Output
3040
Author: LIU, Yaoting
Source: ZOJ Monthly, May 2009
这道题是一道比较经典的树形dp的题。给一棵树,求出这棵树中个数为k的子树的权值的最大值。首先要说的是选出子树的大小为k,而不是从树中任选k个结点,这样是不一样的。。然后需要再次强调的就是dfs的顺序对于dp的顺序影响。本题中可以很容易的得出状态为dp[u][j]表示以u为根节点的有j个结点的子树。然后状态怎么转移呢?假设此时访问到u的子节点v。那么从v中取i个结点。然后需要再从其他结点中选j-i个结点。但是选这j-i个结点该怎么选呢?考虑dp[u][j-i],因为此时访问到v,那么在v之前必然访问过u和其他的一些与u相连的子节点。dp[u][j]此时表示的是已经访问的子树中有j个结点的最大值。通过dfs的不断更新,dp[u][j]不断更新。当u的子节点访问完以后dp[u][i]的值才确定。因而dfs的过程也是子问题不断扩大的过程。考虑动态规划中原问题依赖于子问题。在树形dp中子问题就是dfs的过程中伴随产生的。。随着dfs访问的子节点不断增多,子问题不断扩大。。这样深刻的思考之后发现动态规划的思路是很清晰的。dfs是动态变化,动态更新的,dp的值也是一样。
然后还要强调的是dp的顺序问题。我们得到状态转移方程dp[u][j]=max(dp[u][j],dp[v][i]+dp[u][j-i])。dp[u][j-i]是未访问v之前的那些结点对u的更新结果。如果j从1~k的话,当dp[u][k]调用时,max里边的dp[u][j-i]是被v修改过以后的,这样再加上dp[v][i]的值就不对了。因而在本题的dp过程中还要考虑清楚j要从k~1按照降序来dp。这个原因要理解透彻。
还有本题中需要注意的地方是最后的对所有点的dp值取最大值。这是为啥?dp[v][k]求出来的是v所在子树的最大值。而如果有k个结点还有一些结点是v的祖先节点怎么办。其实dp的含义还是需要再精确一下,这个题中dp的含义还包含u是这k个结点的子树中深度最浅的结点(叶子结点深度最深,这个结点u离根节点比其他k-1个更近)。因而如果v是选出来的这个含有k个结点的子树中的一个结点,并且k个结点中还有结点在v的上方,那么我们不用考虑,这个值就交给v上面那个离根节点最近的祖先结点来完成了。因而还要想到dp表示的状态中这一层隐含的意思。
相信对树形dp研究之后,发现dfs的过程还有许多值得思考回味的地方。
参考代码:
#include<cstdio>#include<iostream>#include<cmath>#include<cstring>#include<algorithm>#include<string>#include<vector>#include<map>#include<set>#include<stack>#include<queue>#include<ctime>#include<cstdlib>#include<iomanip>#include<utility>#define pb push_back#define mp make_pair#define CLR(x) memset(x,0,sizeof(x))#define _CLR(x) memset(x,-1,sizeof(x))#define REP(i,n) for(int i=0;i<n;i++)#define Debug(x) cout<<#x<<"="<<x<<" "<<endl#define REP(i,l,r) for(int i=l;i<=r;i++)#define rep(i,l,r) for(int i=l;i<r;i++)#define RREP(i,l,r) for(int i=l;i>=r;i--)#define rrep(i,l,r) for(int i=l;i>r;i--)#define read(x) scanf("%d",&x)#define put(x) printf("%d\n",x)#define ll long long#define lson l,m,rt<<1#define rson m+1,r,rt<<11using namespace std;int dp[110][110];int n,k;int a[110];vector<int>G[110];void dfs(int u,int rt){ int len=G[u].size(); dp[u][1]=a[u]; rep(i,0,len) { int v=G[u][i]; if(v==rt) continue; dfs(v,u); RREP(j,k,1) REP(l,1,j) dp[u][j]=max(dp[u][j],dp[u][l]+dp[v][j-l]); }}int main(){ while(~scanf("%d%d",&n,&k)) { CLR(dp); rep(i,0,n) read(a[i]); rep(i,0,n-1) { int u,v; scanf("%d%d",&u,&v); G[u].pb(v); G[v].pb(u); } dfs(0,-1); int ans=0; rep(i,0,n) ans=max(ans,dp[i][k]); printf("%d\n",ans); REP(i,0,100) G[i].clear(); }}
- ZOJ 3201解题报告
- zoj 1005 解题报告
- zoj 1027解题报告
- zoj 1108解题报告
- [解题报告]ZOJ 1001
- zoj 3212 解题报告
- zoj 1312解题报告
- ZOJ 1115 解题报告
- zoj 1002解题报告
- ZOJ 1610 解题报告
- ZOJ-1796解题报告
- ZOJ 1649 解题报告
- ZOJ 1048解题报告
- ZOJ 1115解题报告
- ZOJ 2104解题报告
- Friend Number ZOJ 解题报告
- ZOJ 3710 Friends 解题报告
- ZOJ 3717 Balloon 解题报告
- 语言识别之IPP简介
- Temporary-Post-Used-For-Style-Detection-Title-1099924566
- Js实现无刷新添加新层,拖动DIV层可互换位置
- Java的入门基本语法-笔记
- 在 Ubuntu 中手动安装任何版本的 Firefox
- ZOJ 3201解题报告
- 关于Lucene断点续索引和增量索引的问题
- 有利益,就有纷争
- Java的对象和类-笔记
- Android System Property框架
- 安卓基础总结 网络基础 Connector post get
- 找到链表的中点
- Optimizing Composer's autoloader performance
- 安卓基础总结 httpClient上传下载