P8602 [蓝桥杯 2013 省 A] 大臣的旅费

题目描述

很久以前,T 王国空前繁荣。为了更好地管理国家,王国修建了大量的快速路,用于连接首都和王国内的各大城市。

为节省经费,T 国的大臣们经过思考,制定了一套优秀的修建方案,使得任何一个大城市都能从首都直接或者通过其他大城市间接到达。同时,如果不重复经过大城市,从首都到达每个大城市的方案都是唯一的。

J 是 T 国重要大臣,他巡查于各大城市之间,体察民情。所以,从一个城市马不停蹄地到另一个城市成了 J 最常做的事情。他有一个钱袋,用于存放往来城市间的路费。

聪明的 J 发现,如果不在某个城市停下来修整,在连续行进过程中,他所花的路费与他已走过的距离有关,在走第 千米到第 千米这一千米中( 是整数),他花费的路费是 这么多。也就是说走 千米花费 ,走 千米要花费 。

J 大臣想知道:他从某一个城市出发,中间不休息,到达另一个城市,所有可能花费的路费中最多是多少呢?

输入格式

输入的第一行包含一个整数 ,表示包括首都在内的 王国的城市数。

城市从 开始依次编号, 号城市为首都。

接下来 行,描述 国的高速路( 国的高速路一定是 条)。

每行三个整数 ,表示城市 和城市 之间有一条高速路,长度为 千米。

输出格式

输出一个整数,表示大臣J最多花费的路费是多少。

输入输出样例 #1

输入 #1

5
1 2 2
1 3 1
2 4 5
2 5 4

输出 #1

135

说明/提示

样例解释:大臣 J 从城市 到城市 要花费 的路费。

时限 5 秒, 64M。蓝桥杯 2013 年第四届省赛

解析

总体来看,这道题目的意图是求取树的直径,最多的旅费,那肯定对应着最远的距离,所以来拆分一下这道题目:1.找到两个距离最远的点,2.计算这两个点的旅费

旅费来看是等差数列,第x千米花费x+10元,那么这就是一个等差数列求和:

接下来,就是求树的直径,我们通过两次DFS或者树形DP求解。

两次DFS:因为任何一个大城市都能从首都直接或者通过其他大城市间接到达,所以:

  • 从任意一点出发,比如首都1,找到距离首都最远的城市
  • 然后再从P出发,找到距离P最远的城市Q
  • 那么P到Q的距离就是树的直径L

树形 DP:对于每一个节点 ,我们寻找以 为最高点(转折点)的最长路径。

  • 设 为从节点 出发,在其子树中能够到达的最远距离。
  • 设 为从节点 出发,在其子树中能够到达的次远距离。
  • 那么,经过节点 的最长路径长度就是 。
  • 我们遍历所有节点,其中最大的 就是整棵树的直径 。

废话少说,上代码:

代码

两次DFS

//
// Created by 23264 on 2026/1/27.
//
#include<bits/stdc++.h>
using namespace std;
 
struct Edge
{
    int to;
    int weight;
};
 
int n;
vector<vector<Edge>> Road;
int maxx;
int far;
 
void dfs(int u,int p,long long d)
{
    if (d > maxx)
    {
        maxx = d;
        far = u;
    }
    for (auto edge : Road[u])
    {
        if (edge.to != p)
        {
            dfs(edge.to,u,d + edge.weight);
        }
    }
}
 
int main()
{
    cin >> n;
    Road.resize(n + 1);
    for (int i = 0;i < n - 1;i++)
    {
        int Pi,Qi,Di;
        cin >> Pi >> Qi >> Di;
        Road[Pi].push_back({Qi,Di});
        Road[Qi].push_back({Pi,Di});
    }
    maxx = -1;
    dfs(1,-1,0);
    maxx = -1;
    dfs(far,-1,0);
    cout << maxx * 10 + maxx * (maxx + 1) / 2 << endl;
}

树形DP

//
// Created by 23264 on 2026/1/27.
//
#include <bits/stdc++.h>
using namespace std;
 
struct Edge
{
    int to;
    int weight;
};
 
int n;
vector<vector<Edge>> adj;
long long d1[100005];// 第一长路径
long long d2[100005];// 第二长路径
long long maxx = 0;// 最大直径
 
void DP(int u,int p)
{
    d1[u] = d2[u] = 0;
    for (auto &edge : adj[u])
    {
        int v = edge.to;
        if (v == p)
        {
            continue; // 避免回到父节点
        }
 
        DP(v,u); // 先递归子节点
 
        // 计算从 v 上来到 u 的距离
        long long dist = d1[v] + edge.weight;
 
        // 更新 u 的最长和次长路径
        if (dist > d1[u])
        {
            d2[u] = d1[u];
            d1[u] = dist;
        }
        else if (dist > d2[u])
        {
            d2[u] = dist;
        }
    }
    // 更新全局最长直径
    maxx = max(maxx,d1[u] + d2[u]);
}
 
int main()
{
    cin >> n;
    adj.resize(n + 1);
    for (int i = 0;i < n - 1;i++)
    {
        int u,v,w;
        cin >> u >> v >> w;
        adj[u].push_back({v,w});
        adj[v].push_back({u,w});
    }
    DP(1,-1);
    cout << maxx * 10 + maxx * (maxx + 1) / 2 << endl;
}