luogu P1122(树形dp)

传送门

题意:

给你一个有\(n\)个结点的树,每个结点有一个权值,现在你可以把任意一条边断掉,此时会形成两棵树,你需要舍去其中一棵树,并统计另外一棵树上所有结点的权值。现在你可以做若干次上述操作,问你可以获得的最大权值。

分析:

这是一个比较经典的树形\(dp\)的模型。

我们设\(dp[i]\)为以结点\(i\)为根的树的最大权值,那么对于它的儿子,我们有选和不选(不选代表把儿子的子树都割掉了)两种状态。因此我们不难发现,当前的状态可以由儿子的选和不选这两种状态转移过来(若选取该儿子,则我们只需要令权值增加\(dp[son[i]]\)即可,否则增加\(0\)),故有状态转移方程\(dp[i]+=\max(dp[son[i],0])\) 。而我们在进行\(dfs\)的过程正好是完成了自底向上更新的过程,因此我们只需要在\(dfs\)的过程中进行更新即可。

代码

#include <bits/stdc++.h>
#define maxn 16005
using namespace std;
struct Node{
    int to,next;
}q[maxn<<1];
int head[maxn],cnt=0,dp[maxn],val[maxn],tmp[maxn],n;
int vis[maxn],maxx=0;
void add_edge(int from,int to){
    q[cnt].to=to;
    q[cnt].next=head[from];
    head[from]=cnt++;
}
void init(){
    memset(head,-1,sizeof(head));
    cnt=0;
}
void dfs(int x){
    dp[x]=val[x];
    vis[x]=1;
    for(int i=head[x];i!=-1;i=q[i].next){
        int to=q[i].to;
        if(vis[to]) continue;
        dfs(to);
        dp[x]+=max(dp[to],0);
    }
    maxx=max(maxx,dp[x]);
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d",&val[i]);
    }
    init();
    for(int i=1;i<n;i++){
        int from,to;
        scanf("%d%d",&from,&to);
        add_edge(from,to);
        add_edge(to,from);
    }
    dfs(1);
    printf("%d\n",maxx);
    return 0;
}


posted @ 2019-07-13 15:47  ChenJr  阅读(121)  评论(0编辑  收藏  举报