树形dp练习

洛谷P1352 没有上司的舞会

经典的树形dp

f[x][0]表示以x为根的子树,且x不参加舞会的最大快乐值

f[x][1]表示以x为根的子树,且x参加了舞会的最大快乐值

则f[x][0]=sigma{max(f[y][0],f[y][1])} (y是x的儿子)

f[x][1]=sigma{f[y][0]}+h[x] (y是x的儿子)

先找到唯一的树根root

则ans = max(f[root][0], f[root][1])

#include <bits/stdc++.h>
using namespace std;
const int maxn = 6e3 + 7;
struct edge
{
    int to, next;
}e[maxn];
int r[maxn], head[maxn], cnt, f[maxn][2];
bool mark[maxn], vis[maxn];

void add(int u, int v)
{
    e[++cnt].next = head[u];
    e[cnt].to = v;
    head[u] = cnt;
}

void dfs(int x)
{
    vis[x] = 1;
    if(!head[x]) //x为叶子结点
    {
        f[x][0] = 0;
        f[x][1] = r[x]; //所以可以简化为无需r数组,开始直接读入f[x][1],这样最后也不用+=r[x]了
        return;
    }
    for(int i = head[x]; i; i = e[i].next)  //遍历以x为起点的所有边
    {
        int y = e[i].to;
        if(vis[y]) continue;
        dfs(y);   //在下两行使用y前,保证y已经计算出来
        f[x][0] += max(f[y][0], f[y][1]);
        f[x][1] += f[y][0];
    }
    f[x][1] += r[x];
    return;
}

int main()
{
    int n, a, b;
    cin >> n;
    for(int i = 1; i <= n; ++i) scanf("%d", &r[i]);
    for(int i = 1; i <= n - 1; ++i)
    {
        scanf("%d %d", &a, &b);
        add(b, a);  //加边是单向的,a是b的父亲,b不是a的父亲
        mark[a] = 1;
    }
    for(int i = 1; i <= n; ++i)
    {
        if(!mark[i])  //找到根
        {
            dfs(i);
            printf("%d\n", max(f[i][0], f[i][1]));
            return 0;
        }
    }
}

 

posted @ 2020-12-23 14:33  .Ivorelectra  阅读(76)  评论(0编辑  收藏  举报