您的位置:首页 > 其它

ZOJ 3201解题报告

2015-03-17 00:47 316 查看
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 1
10 20 30
0 1
0 2
3 2
10 20 30
0 1
0 2

Sample Output
30
40


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<<11
using 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();
    }
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: