ZOJ 3201解题报告

来源:互联网 发布:淘宝ceo 编辑:程序博客网 时间:2024/05/23 13:42

Tree of Tree

Time Limit: 1 Second      Memory Limit: 32768 KB

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();    }}

0 0