旅行

旅行

给定一个 $n$ 个节点的树,节点编号为 $1 \sim n$。

请你从中选择一个简单路径(不能包含重复节点或重复的边),并沿所选路径来一场旅行,更具体的说,就是从所选路径的一个端点沿路径前往另一个端点。

注意,所选简单路径可以只由一个节点组成。

旅行需要花费能量。

初始时,你的能量为 $0$。

在旅行过程中:

  • 在经过任何一条边之前,你的现有能量都不能少于该边所需消耗的能量(否则,将无法顺利通过该边)。
  • 在满足条件 $1$ 的前提下,旅行结束时,剩余的能量尽可能大。

请计算并输出剩余能量的最大可能值。

输入格式

第一行包含整数 $n$。

第二行包含 $n$ 个整数 $w_1,w_2, \dots ,w_n$。

接下来 $n−1$ 行,每行包含三个整数 $u,v,c$,表示存在一条边 $(u,v)$,经过它所需的能量为 $c$。

保证给定图是一棵树。

输出格式

一个整数,表示剩余能量的最大可能值。

数据范围

前 $3$ 个测试点满足 $1 \leq n \leq 5$。
所有测试点满足 $1 \leq n \leq 3 \times {10}^{5}$,$0 \leq w_i \leq {10}^{9}$,$1 \leq u,v \leq n$,$u \ne v$,$1 \leq c \leq {10}^{9}$。

输入样例1:

3
1 3 3
1 2 2
1 3 2

输出样例1:

3

输入样例2:

5
6 3 2 5 0
1 2 10
2 3 3
2 4 1
1 5 1

输出样例2:

7

 

解题思路

  比赛的时候没细想,然后蒙对了做法。

  如果不考虑第一个条件,这个问题就变得很简单,就是找一条权值最大的路径,类似于求树的直径。现在我先不考虑这个条件,直接求权值最大的路径,然后看一下可不可以证明这个最大路径一定是满足这个约束条件的,如果满足这个条件意味着我们可以忽略这个约束条件直接求就可以了。

  可以发现把这段能量为负的部分去掉,整个路径只留后半段,结果一定会比保留这部分的要优。因此如果存在走完某条路径能量变负数,意味着这个路径一定不是最优解,因为我们可以构造出能量更大的路径。因此最优解一定满足在中间任意时刻总能量都是大于等于$0$的。

  所以可以用树形dp来求出一条权值最大的路径。

  AC代码如下:

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 typedef long long LL;
 5 
 6 const int N = 3e5 + 10, M = N * 2;
 7 
 8 int head[N], e[M], wts[M], ne[M], idx;
 9 int w[N];
10 LL f[N];
11 LL ans;
12 
13 void add(int v, int w, int wt) {
14     e[idx] = w, wts[idx] = wt, ne[idx] = head[v], head[v] = idx++;
15 }
16 
17 void dfs(int u, int pre) {
18     LL d1 = 0, d2 = 0;
19     for (int i = head[u]; i != -1; i = ne[i]) {
20         if (e[i] != pre) {
21             dfs(e[i], u);
22             LL t = f[e[i]] - wts[i];
23             if (t >= d1) d2 = d1, d1 = t;
24             else if (t >= d2) d2 = t;
25         }
26     }
27     
28     f[u] = d1 + w[u];
29     ans = max(ans, d1 + d2 + w[u]);
30 }
31 
32 int main() {
33     int n;
34     scanf("%d", &n);
35     for (int i = 1; i <= n; i++) {
36         scanf("%d", w + i);
37     }
38     memset(head, -1, sizeof(head));
39     for (int i = 0; i < n - 1; i++) {
40         int v, w, wt;
41         scanf("%d %d %d", &v, &w, &wt);
42         add(v, w, wt), add(w, v, wt);
43     }
44     
45     dfs(1, -1);
46     printf("%lld", ans);
47     
48     return 0;
49 }

 

参考资料

  AcWing 4620. 旅行(AcWing杯 - 周赛):https://www.acwing.com/video/4404/

posted @ 2022-09-25 15:08  onlyblues  阅读(48)  评论(0编辑  收藏  举报
Web Analytics