travel Gym - 247728H(树形DP)

travel Gym - 247728H

White Cloud has a tree with n nodes.The root is a node with number 1. Each node has a value.

White Rabbit wants to travel in the tree 3 times. In Each travel it will go through a path in the tree.

White Rabbit can’t pass a node more than one time during the 3 travels. It wants to know the maximum sum value of all nodes it passes through.

Input
The first line of input contains an integer n(3≤n≤400001)
In the next line there are n integers in range [0,1000000] denoting the value of each node.

For the next n−1 lines, each line contains two integers denoting the edge of this tree.

Output
Print one integer denoting the answer.

Example
Input
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
Output
110

求一棵树中三条不相交的路径的最大权值和

  • dp[ i ][ j ][ 0 ]表示 以 i 点为根的树中 j 条不相交路径的最大权值和
  • dp[ i ][ j ][ 1 ]表示以 i 点为根的树中 j 条不相交路径 + 一条以 i 点为端点的路径的最大权值和 (显然,dp[ i ][ j ][ 1 ]是dp[i][j+1][0]的子集)
  • 从叶子结点往根节点进行DP
  • 当DP完每个节点的子树后,每个节点有四种可以选择的情况
    • 1、 当前节点空出来,不作为任何一条链上的点
    • 2、当前节点单独作为一条链
    • 3、当前节点并上其子树中的一条链(需要存在一条链,以其子节点为端点)
    • 4、当前节点连接其子树中的两条链,使其成为一条链(需要存在两条链,分别以其两个子节点为端点)
  • 显然,dp[i][j][0]包含以上四种情况
  • 由于dp[i][j][1]必须有一条链以 i 为端点,只包含以上2、3两种情况
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e6+9;
long long val[maxn];
vector<long long>edge[maxn];
long long dp[maxn][5][2];

void bfs(long long x,long long fa)
{
    long long son[5][3];
    long long tmp[5][3];
    long long ans[5][3];
    memset(ans,0,sizeof(ans));
    memset(tmp,0,sizeof(tmp));
    memset(son,0,sizeof(son));//
    for(long long ii = 0; ii<edge[x].size(); ii++)
    {
        long long to = edge[x][ii];
        if(to==fa)
            continue;
        bfs(to,x);
        for(long long i = 0; i<=3; i++)
        {
            son[i][0] = dp[to][i][0];
            son[i][1] = dp[to][i][1];
        }
        printf("%lld:\n",x);
        for(long long i = 0; i<=3; i++)
        {
            for(long long j = 0; j+i<=3; j++)
            {
                for(long long k = 0; k<=2; k++)
                {
                    for(long long l = 0; k+l<=2; l++)
                    {
                        //因为son[i][0]和son[i][1]中会有相交的部分,所以必须只用上一步得到的ans和当前的son[i][j]进行更新(不能自身更新自身),用tmp保存下来,最后在复制到ans
                        tmp[i+j][k+l] = max(tmp[i+j][k+l],son[i][k]+ans[j][l]);
                    }
                }
            }
        }
        memcpy(ans,tmp,sizeof(ans));
        //ans[i][j]表示当前节点的子树有i条链不必以其子节点为端点(可以可不以),有j条链以其子节点为端点的最大权值
    }
    for(long long i = 0; i<=3; i++)
    {
        for(long long j = 0; j<=2; j++)
        {
            printf("ans[%lld][%lld] = %lld\n",i,j,ans[i][j]);
        }
    }
    //求dp[x][i][0],当前节点可以是一条链的端点,也可以不是
    for(long long i = 0; i<=3; i++) //由其子树构成i条链的最大值(不含当前节点)
    {
        dp[x][i][0] = ans[i][0];
    }

    for(long long i = 1; i<=3; i++)//加上当前节点,所构成的i条链的最大值
    {
        for(long long j = 0; j<=2; j++)
        {
            //j=0时,当前节点是与其它链不相交的一条单独的链
            //j=1时,当前节点并上以其子节点为终端的一条链
            //j=2时,当前节点连接两条以其两个子节点为终端的链,使其成为一条链
            dp[x][i][0] =max(dp[x][i][0],ans[i-1][j]+val[x]);
        }
    }

    //求dp[x][i][1],当前节点必须是一条链的端点
    for(long long i = 0; i<=3; i++)
    {
        for(long long j = 0; j<=1; j++)
        {
            //j=0时,当前节点是与其它链不相交的一条单独的链
            //j=1时,当前节点并上以其子节点为终端的一条链
            dp[x][i][1] = max(dp[x][i][1],ans[i][j]+val[x]);
        }
    }
    for(long long i = 0; i<=3; i++)
    {
        printf("dp[%lld][%lld][0] = %lld\n",x,i,dp[x][i][0]);
        printf("dp[%lld][%lld][1] = %lld\n",x,i,dp[x][i][1]);
    }


}
int main()
{
    long long i,j,m,n,a,b;
    ios::sync_with_stdio(false);
    cin>>n;
    for(i = 1; i<=n; i++)
    {
        cin>>val[i];
    }
    for(i = 1; i<n; i++)
    {
        cin>>a>>b;
        edge[a].push_back(b);
        edge[b].push_back(a);
    }
    bfs(1,0);
    cout<<dp[1][3][0]<<endl;

    return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章