点对最大值(“科林明伦杯”哈尔滨理工大学第十届程序设计竞赛-A)(树形dp、动态规划)

时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 131072K,其他语言262144K
64bit IO Format: %lld
OJ: 牛客

题目描述

这里有一棵树,每个点和每条边都存在一个价值。对于树上点对的价值,包括点对的起点和终点以及路径上边权值之和,不包括路径上其他点值。
求这颗树上最大的点对价值为多少。点对至少需要两个点。

输入描述:

输入 t t t,代表有 t t t组样例。每组样例第一行输入 n n n,代表有 n n n个点。接下来有 n − 1 n-1 n1行,第 i i i行有a[i]b[i],代表a[i]节点与 i i i节点存在一条边,且边的值为b[i] 2 < = i < = n 2<=i<=n 2<=i<=n。接下来一行有 n n n个值c[j],代表每个节点 j j j的价值, 1 < = j < = n 1<=j<=n 1<=j<=n
( t < = 10 , n > 1 , n < 1 e 6 , a [ i ] < i , − 500 < = b [ i ] < = 500 , − 500 < = c [ j ] < = 500 ) (t<=10,n>1,n<1e6,a[i]<i,-500<=b[i]<=500,-500<=c[j]<=500) t<=10n>1n<1e6a[i]<i500<=b[i]<=500500<=c[j]<=500

输出描述:

输出最大的点对价值

输入

1
4
1  -2
1   2
1   3
2 -2 3 4

输出

12

题解

树形dp。

包含节点u的最大权值的路径有2种:

  1. 以点u为端点的路径
  2. 不以u为端点的路径

对于第一种情况,最大值是(以子节点为端点的路径的最大值加上此端点到子节点的边的权值)的最大值。

不过需要特别注意连接时要避开计算节点的权值(计算过程中我只保存了远端端点的权值)。我们只需要计算路径上边的权值和即可,节点的权值只计算两端的端点的权值。

对于第二种情况,取(以子节点为端点的路径的最大值加上此端点到子节点的边的权值)的前两大,然后让他俩连起来。

然后回溯求解,最后答案就是根节点的两种情况下的权值最大值。

代码

//牛客明伦杯-树形dp-点对
#include <bits/stdc++.h>
#define m_p make_pair
#define p_i pair<int, int>
#define _for(i, a) for(register int i = 0, lennn = (a); i < lennn; ++i)
#define _rep(i, a, b) for(register int i = (a), lennn = (b); i <= lennn; ++i)
#define outval(a) cout << "Debuging...|" << #a << ": " << a << "\n"
#define mem(a, b) memset(a, b, sizeof(a))
#define mem0(a) memset(a, 0, sizeof(a))
#define fil(a, b) fill(a.begin(), a.end(), b);
#define scl(x) scanf("%lld", &x)
#define sc(x) scanf("%d", &x)
#define abs(x) ((x) > 0 ? (x) : -(x))
#define PI acos(-1)
#define lowbit(x) (x & (-x))
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
const int maxn = 1000005;
const int maxm = 1000005;
const int maxp = 30;
const int inf = 0x3f3f3f3f;
const LL INF = 0x3f3f3f3f3f3f3f3f;
const int mod = 1000000007;
const double eps = 1e-8;
const double E = 2.718281828;

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

int n;
LL c[maxn];
LL sum[maxn];
vector<int> ps;
LL ans;
vector< pair< int, LL > > G[maxn];

LL dfs(int u, int fa) {
    
    
    ps.push_back(u);
    int pos = ps.size();
    LL m = -INF, fm = -INF, sm = -INF;  //计算前两大(以子节点为端点的路径的最大值加上此端点到子节点的边的权值) 只保存远端端点的权值,不保存路径上端点的权值,也不保存近端端点的权值。
    _for(i, G[u].size()) {
    
    
        int v = G[u][i].first;
        if(v == fa) continue;
        LL w = G[u][i].second, tem = -INF;
        tem = dfs(v, u) + w;            //延申以点`v`为端点的路径
        if(tem > fm) sm = fm, fm = tem;
        else if(tem > sm) sm = tem;
    }
    ans = max(ans, fm + c[u]);      //第一种情况:以点`u`为端点的路径+端点u的权值
    ans = max(ans, fm + sm);        //第二种情况:不以`u`为端点的路径
    return max(fm, c[u]);           //返回以当前节点为端点的权值的最大值(只含有远端的端点的权值,不计算路径上的点的权值)
}

void init() {
    
    
    _rep(i, 1, n) G[i].clear();
    ans = -INF;
}

void sol() {
    
    
    init();
    _rep(i, 2, n) {
    
    
        int a = read();
        LL b = read();
        G[i].push_back(m_p(a, b));
        G[a].push_back(m_p(i, b));
    }
    _rep(i, 1, n) c[i] = read();
    dfs(1, 1);
    printf("%lld\n", ans);
}

int main() {
    
    
    //ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
#ifdef ONLINE_JUDGE
#else
    freopen("in.txt", "r", stdin);
#endif

    int T;
    while(cin>>T) {
    
    
        _for(i, T) {
    
    
            n = read();
            sol();
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_42856843/article/details/109330657