牛客多校Round 2

Solved:3

rank:187

H.travel

题意:给一颗带有点权的树 找三条不相交的链 使得点权最大

题解:使用树形DP dp[x][i][0/1] 表示x节点选择i条链 有没有经过x的链

   对于每一个回溯的状态 下面的结点已经处理好了

   但是处理当前结点时还有一种有两条经过x的链 实际上他们可以合成一条链

   为了避免重复计算 经过当前结点的权值在更新时计算

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

int n;
ll val[400005];
ll dp[400005][4][2];
vector<int> g[400005];

void dfs(int x, int fa)
{
    ll tmp[4][3]; ll f[4][3];
    for(int i = 0; i < 4; i++) tmp[i][0] = tmp[i][1] = tmp[i][2] = 0;
    for(int i = 0; i < g[x].size(); i++)
    {
        int v = g[x][i];
        if(v == fa) continue;
        dfs(v, x);
        memcpy(f, tmp, sizeof(tmp));

        for(int a = 0; a < 4; a++)
            for(int b = 0; b < 4; b++)
            {
                if(a + b > 3) continue;
                for(int c = 0; c < 3; c++)
                    for(int d = 0; d < 2; d++)
                    {
                        if(c + d > 2) continue;
                        tmp[a + b][c + d] = max(tmp[a + b][c + d], f[a][c] + dp[v][b][d]);
                    }
            }
    }
    for(int i = 0; i < 4; i++)
    {
        dp[x][i][0] = max(dp[x][i][0], tmp[i][0]);
        dp[x][i][1] = max(dp[x][i][1], tmp[i][1] + val[x]);

        if(i < 3)
        {
            for(int j = 0; j < 2; j++)
                for(int k = j + 1; k < 3; k++)
                    dp[x][i + 1][j] = max(dp[x][i + 1][j], tmp[i][k] + val[x]);
        }
    }
}

int main()
{
    scanf("%d", &n);
    for(int i = 1; i <= n; i++) scanf("%lld", &val[i]);
    for(int i = 1; i < n; i++)
    {
        int u, v;
        scanf("%d%d", &u, &v);
        g[u].push_back(v);
        g[v].push_back(u);
    }
    dfs(1, -1);
    ll ans = 0;
    for(int i = 0; i < 4; i++) ans = max(ans, dp[1][i][0]);
    printf("%lld
", ans);
    return 0;
}
/*
13
10 10 10 10 10 1 10 10 10 1 10 10 10
1 2
2 3
3 4
4 5
2 6
6 7
7 8
7 9
6 10
10 11
11 12
11 13
*/
View Code

   

原文地址:https://www.cnblogs.com/lwqq3/p/9349715.html