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;
}

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