树上子链【树形DP】

树上子链

备注:一个结点,也可以称作一条链


题意:

  • 在一棵树上,找到结点权值和最大的子链。

思路:

  • 将无根树转换为有根树,任意结点为根
  • 在有根树上,设fir[ u ]为在结点u这颗子树上,包含结点u的权值和最大的子链。
  • 同样地,设sec[ u ]为在结点u这颗子树上,包含结点u的权值和第二大的子链。
  • 可以得到dp方程:ans = max(val[u], fir[u], fir[u] + sec[u] - val[u])

注意:

  • ans初始化为负无穷,结点权值是有赋值的嘛
  • ans = max(ans, val[ u ])放在跑儿子结点循环的外面,否则,如果结点个数为1,那么输出-INF,显然错了

END


#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#define INF 0x3f3f3f3f
using namespace std;

typedef long long ll;

inline int read()
{
    int x = 0, f = 1; char c = getchar();
    while(c < '0' || c > '9') { if(c == '-') f = -f; c = getchar(); }
    while(c >= '0' && c <= '9') { x = x * 10 + c - '0'; c = getchar(); }
    return x * f;
}

const int maxN = 100005;

int n, val[maxN];

struct EDGE{
    int adj, to;
    EDGE(int a = -1, int b = 0): adj(a),to(b){}
}edge[maxN << 1];
int head[maxN], cnt;

void add_edge(int u, int v)
{
    edge[cnt] = EDGE(head[u], v);
    head[u] = cnt ++ ;
}

void init()
{
    memset(head, -1, sizeof(head));
    cnt = 0;
}

ll fir[maxN], sec[maxN];
ll ans = ll(-INF);

void dfs(int u, int fa)
{
    fir[u] = sec[u] = (ll)val[u];
    ans = max(ans, (ll)val[u]);
    for(int i = head[u]; ~i; i = edge[i].adj)
    {
        int v = edge[i].to;
        if(v == fa) continue;
        dfs(v, u);
        if(fir[u] < fir[v] + (ll)val[u])
        {
            sec[u] = fir[u];
            fir[u] = fir[v] + (ll)val[u];
        }
        else if(sec[u] < fir[v] + (ll)val[u])
            sec[u] = fir[v] + (ll)val[u];
        ans = max(ans, max(fir[u], fir[u] + sec[u] - (ll)val[u]));
    }
}

int main()
{
    init();
    n = read();
    for(int i = 1; i <= n; ++ i )
        val[i] = read();
    for(int i = 1; i < n; ++ i )
    {
        int u, v;
        u = read(); v = read();
        add_edge(u, v);
        add_edge(v, u);
    }
    dfs(1, 0);
    printf("%lld\n", ans);
    return 0;
}
发布了242 篇原创文章 · 获赞 68 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/weixin_44049850/article/details/104460822