牛客网暑期ACM多校训练营(第二场) H travel 树形DP

时间限制:C/C++ 2秒,其他语言4秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld

题目描述

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.


输入描述:

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.

输出描述:

Print one integer denoting the answer.


输入例子:
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


输出例子:
110

题意 一棵树 给出每个节点的点权 要求三条不想交链的最大和。

对于我来说已经是神仙题了。
解题思路:
首先,如果对树形DP有一定了解的话 肯定会将子链数加入状态。
可以得出最开始的一个状态定义
dp[i][j] 表示 以i点为根节点的子树,选出k条链的状态最大权值和。
但这样根本无法转移。
因为 当我们考虑从子节点状态 推出父亲节点状态时,如果要把当前父节点并入子链中时,我们定义的状态是显然不够用的。 因此要扩充状态数
开始考虑如何扩充。
考虑跟节点加入子节点的几种情况
1. 单独出来 成为一条只有一个点的链
2. 并入一条以子节点为端点的链
3. 链接两条以子节点为端点的链
第一种情况很好转移
但对于第二三种情况 用现有状态是无法转移的
这样 就可以定义出我们需要扩展的状态 link[i][j] 表示以i节点为跟的子树 有取j条链再加上一条以以根节点和叶子节点为端点链的最大取值。
就很好转移了。
PS 我总感觉这道题的数据有点水。

#include <bits/stdc++.h>
#include <algorithm>
#include <math.h>
using namespace std;
const int MAX=4e5+10;
class Edge{
public:
    int u,v,next;
};
Edge edge[MAX<<1];
int head[MAX];
int tot;
void add(int u,int v){
    edge[tot].u=u;
    edge[tot].v=v;
    edge[tot].next=head[u];
    head[u]=tot;
    tot++;
}
void init(){
    memset(head,-1,sizeof head);
    tot=0;
}
long long W[MAX];
long long link[MAX][4];
long long dp[MAX][4];
void dfs(int u,int pre){
    long long per[4][4];
    long long now[4][4];
    long long cnt[4][4];
    memset(per,0,sizeof per);
    memset(now,0,sizeof now);
    memset(cnt,0,sizeof cnt);
    for(int i= head[u];i!=-1; i=edge[i].next){
        int v=edge[i].v;
        if(v==pre) continue;
        dfs(v,u);
        memset(cnt,0,sizeof cnt);
        for(int j=0;j<4;j++){
            cnt[j][0]=dp[v][j];
            cnt[j][1]=link[v][j];
        }
        memset(now,0,sizeof now);
        for(int j=0;j<4;j++){
            for(int k=0;k+j<4;k++){
                for(int s=0;s<=2;s++){
                    for(int t=0;s+t<=2;t++){
                        //cout<<"cnt:"<<k<<","<<t<<":"<<cnt[k][t]<<endl;
                        now[j+k][s+t]=max(now[j+k][s+t],per[j][s]+cnt[k][t]);
                    }
                }
            }
        }
        for(int j=0;j<4;j++){
            for(int k=0;k<4;k++){
                //cout<<per[j][k]<<" ,"<<now[j][k]<<endl;
                per[j][k]=now[j][k];
            }
        }
    }
    // 转移dp数组
    // 情况1 : 没有竖直链的情况
    for(int i=0;i<4;i++){
        dp[u][i]=per[i][0];
    }
    for(int i=0;i<4;i++){
        dp[u][i]=max(dp[u][i],per[i-1][0]+W[u]);
    }
    // 2 : 竖直链合并
    for(int i=1;i<4;i++){
        for(int k=1;k<=2;k++)
        dp[u][i]=max(dp[u][i],per[i-1][k]+W[u]);
    }
    // 转移link数组
    for(int i=0;i<4;i++){
        link[u][i]=per[i][1]+W[u];
    }
}
int main(){
    int n;
    init();
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%lld",&W[i]);
    }
    for(int i=1;i<n;i++){
        int u,v;
        scanf("%d %d",&u,&v);
        add(u,v);
        add(v,u);
    }
    dfs(1,-1);
    cout<<dp[1][3]<<endl;
    return 0;
}

猜你喜欢

转载自blog.csdn.net/lifelikes/article/details/81164189
今日推荐