【题解 && dp】 dp例题 4

本文介绍了一道关于树形动态规划的问题,旨在寻找每个根节点的最长链和次长链,确保两者不共用边。文章提供了详细的算法思路及C++实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述:

大神zcc and zyb(my granddaughter)给定⼀棵n个点的边权树,由于他(她)太强了,所以他想考考你。
求每一个以i为根节点的子树中以i为起点的最长链和次长链是多少?
注: 最长链和次长链必须是彼此没有相同边的两条独立的链。


样例

输入格式
第一行输入一个T((1&lt;&lt;T)≤220(1&lt;&lt;T)≤2^{20}(1<<T)220),表示有T组数据
对于每组数据,输入总共n行 。 第一行有两个整数n和S,分别表示总点数和树根。
第2 ~ n行每行有3个整数x,y,z,表示x到y有一条边,边权为z(0&lt;=z&lt;=100000&lt;=z&lt;=100000<=z<=10000)。

输出格式
输出总共T∗2T*2T2
对于每组数据,输出两行
第一行输出n个数,第i个数表示以i号点作为根节点的子树的以i为起点的最长链,数之间用2个空格隔开
第二行输出n个数,第i个数表示以i号点作为根节点的子树的以i为起点的次长链,数之间用2个空格隔开


数据规模与约定

%15n&lt;=10\%15 n&lt;=10%15n<=10 (这是用来给你们找错误的 --by 凉心出题人)
%25n&lt;=100\%25 n&lt;=100%25n<=100
%35n&lt;=500\%35 n&lt;=500%35n<=500
%45n&lt;=1000\%45 n&lt;=1000%45n<=1000
%55n&lt;=5000\%55 n&lt;=5000%55n<=5000
%65n&lt;=10000\%65 n&lt;=10000%65n<=10000
%75n&lt;=50000\%75 n&lt;=50000%75n<=50000
%100n&lt;=100000,(1&lt;&lt;T)&lt;=220\%100 n&lt;=100000, (1&lt;&lt;T)&lt;=2^20%100n<=100000,(1<<T)<=220
注意:对于上述所有输出,行末无空格。
时间限制:1s
空间限制:256MB


分析:

这是一道树形dp的题。
然后这道题目其实有个歧义:就是最长链和次长链是否可以重边。
在这里是不能重边的。

那么我们设:
maxx1[i]maxx1[i]maxx1[i]表示以i为根的最长链是多少
maxx2[i]maxx2[i]maxx2[i]表示以i为根的次长链是多少

因为树形dp都是从下往下更新的过程,所以对于一个根x,它的最长链也只与它的儿子有关。

显然,对于一个根x,它的最长链或者次长链只有通过它儿子的最长链才能更新。
并且它儿子的最长链只能更新它的最长链或者次长链(因为不能有重边,显然它的儿子之间不会有重边)。

那么我们可以推得一下公式:
y∈son(x){maxx2[x]=maxx1[x] ,maxx1[x]=maxx1[y]+e[i].v (maxx1&lt;maxx1[y]+e[i].v)maxx2[x]=maxx1[y]+e[i].v (maxx2[x]&lt;maxx1[y]+e[i].v) y\in son(x) \begin{cases} maxx2[x]=maxx1[x] \ , maxx1[x]=maxx1[y]+e[i].v \text{ (maxx1&lt;maxx1[y]+e[i].v)} \\ maxx2[x]=maxx1[y]+e[i].v \text{ (maxx2[x]&lt;maxx1[y]+e[i].v)} \end{cases} yson(x){maxx2[x]=maxx1[x] ,maxx1[x]=maxx1[y]+e[i].v maxx1<maxx1[y]+e[i].vmaxx2[x]=maxx1[y]+e[i].v maxx2[x]<maxx1[y]+e[i].v


Code

#include<bits/stdc++.h>
using namespace std;
struct node{
    int y,v,Next;
}e[200010];
int linkk[100010];
int maxx1[100010];
int maxx2[100010]; 
int t;
int n,root;
int len=0;
void insert(int x,int y,int v){
    e[++len].Next=linkk[x];
    linkk[x]=len;
    e[len].y=y;
    e[len].v=v;
}
void dp(int x,int fa){
    for (int i=linkk[x];i;i=e[i].Next){
	    int y=e[i].y;
	    if (y==fa) continue;
	    dp(y,x);
	    if (maxx1[y]+e[i].v>maxx1[x])
	      maxx2[x]=maxx1[x],maxx1[x]=maxx1[y]+e[i].v;
	    else if (maxx1[y]+e[i].v>maxx2[x])
	      maxx2[x]=maxx1[y]+e[i].v;
	}
}
int main(){
    freopen("T4.in","r",stdin);
    freopen("T4.out","w",stdout);
    scanf("%d",&t);
    while (t--){
	    len=0;
	    scanf("%d %d",&n,&root);
	    for (int i=1;i<=n;i++) linkk[i]=0,maxx1[i]=0,maxx2[i]=0;
	    for (int i=1,x,y,z;i<n;i++)
	      scanf("%d %d %d",&x,&y,&z),insert(x,y,z),insert(y,x,z);
	    dp(root,0);
	    printf("%d",maxx1[1]);
	    for (int i=2;i<=n;i++)
	      printf("  %d",maxx1[i]);
	    printf("\n");
	    printf("%d",maxx2[1]);
	    for (int i=2;i<=n;i++)
	      printf("  %d",maxx2[i]);
	    if (t) printf("\n");
	}
	fclose(stdin);
	fclose(stdout);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值