当前位置: 首页 > news >正文

AcWing 4620. 旅行 树形DP,记忆化搜索

题目描述

给定一个 n 个节点的树,节点编号为 1∼n。

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

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

旅行需要花费能量。

初始时,你的能量为 0。

在旅行过程中:

  • 每经过一个节点(包括起点和终点),就可以得到该节点的能量,其中节点 i 包含的能量为 wi。
  • 每经过一条边 (u,v),就需要消耗一定的能量 c。
    你设计的旅行路线应满足:

在经过任何一条边之前,你的现有能量都不能少于该边所需消耗的能量(否则,将无法顺利通过该边)。
在满足条件 1 的前提下,旅行结束时,剩余的能量尽可能大。
请计算并输出剩余能量的最大可能值。
输入格式
第一行包含整数 n。

第二行包含 n 个整数 w1,w2,…,wn。

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

保证给定图是一棵树。

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

数据范围
前 3 个测试点满足 1≤n≤5。
所有测试点满足 1≤n≤3×105,0≤wi≤109,1≤u,v≤n,u≠v,1≤c≤109。

输入样例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的限制,还能转化成求树的直径的问题吗?

我们现在想一下,在求树的直径的过程中,会经过权值为负数的点嘛?
显然是不会的,那么我们在遍历的过程中,遇到负数的情况,直接跳过就行了。

yxc那个解释我听不明白,他说的去掉,我感觉只是代码上的等价,因为那段代码去掉之后,确实不影响正确性。

出题人就是有可能给你加一个多余的限制条件来阻碍你的思维,但是其实这个条件是没有用的。这就是和别人差距的地方。

时间复杂度

代码

#include <iostream>
#include <cstring>

using namespace std;

typedef long long LL;

const int N = 300010, M = N << 1;

int n;
int v[N];
int h[N], e[M], ne[M], w[M], idx;
LL f[N]; // 到达N剩余的最大能量
LL ans;

void add(int a, int b, int c) {
    e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++;
}

void dfs(int u, int fa) {
    
    LL max1 = 0, max2 = 0;
    
    for (int i = h[u]; ~i; i = ne[i]) {
        int j = e[i];
        if (j == fa) continue;
        // 求子节点的, 这个很关键。
        dfs(j, u);
        
        // 这个去掉也是可以的
        // if (f[j] - w[i] < 0) continue;
        // 更新最大值
        if (f[j] - w[i] >= max1) {
            max2 = max1;
            max1 = f[j] - w[i];
        }
        // 更新次大值
        else if (f[j] - w[i] > max2) {
            max2 = f[j] - w[i];
        }
    }
    
    // 更新全局ans
    ans = max(ans, v[u] + max1 + max2);
    // f[u] 只会计算一次。
    f[u] = v[u] + max1;
}

int main() {
    cin >> n;
    memset(h, -1, sizeof h);
    
    for (int i = 1; i <= n ; i ++ ) scanf("%d", &v[i]);
    
    for (int i = 1; i < n; i ++ ) {
        int a, b, c;
        scanf("%d%d%d", &a, &b, &c);
        add(a, b, c);
        add(b, a, c);
    }
    
    dfs(1, -1);
    
    cout << ans << endl;
    
    return 0;
}


相关文章:

  • 使用微信小程序播放视频直播
  • 代码随想录1.5——数组:35搜索插入位置、34在排序数组中查找元素的第一个和最后一个位置、26.删除排序数组中的重复项、283移动零
  • 信号示波器MSOX3022T是德MSOX3022T混合信号示波器2+16通道
  • 计算机毕业设计之java+javaweb的大学运动场地管理系统
  • 第三章-存储系统-Cache和页式存储、虚拟存储
  • 基于springboot的学生选课系统设计与实现-计算机毕业设计源码+LW文档
  • 【FPGA教程案例87】加解密1——基于FPGA的AES加解密算法verilog实现
  • 【Linux】进程控制 (万字详解)—— 进程创建 | 进程退出 | 进程等待 | 程序替换 | 实现简易shell
  • 在互联网上少了这一步,你就别想着赚钱?
  • Java Stram之“筛选与切片”的简介说明
  • C++ Reference: Standard C++ Library reference: C Library: cfenv: FE_INVALID
  • 吸血、迁移与资本局 Move 公链大火背后
  • 妥协型人格分析,妥协型性格的缺点和改善
  • Kaggle 新手入门必看,手把手教学
  • JAVA 实现《warcraft java版》游戏
  • 「前端」从UglifyJSPlugin强制开启css压缩探究webpack插件运行机制
  • 【前端学习】-粗谈选择器
  • 002-读书笔记-JavaScript高级程序设计 在HTML中使用JavaScript
  • 345-反转字符串中的元音字母
  • Create React App 使用
  • CSS相对定位
  • Java|序列化异常StreamCorruptedException的解决方法
  • JavaScript类型识别
  • JavaScript学习总结——原型
  • JDK9: 集成 Jshell 和 Maven 项目.
  • js
  • js数组之filter
  • Service Worker
  • Spark学习笔记之相关记录
  • UMLCHINA 首席专家潘加宇鼎力推荐
  • 二维平面内的碰撞检测【一】
  • 关于使用markdown的方法(引自CSDN教程)
  • 腾讯优测优分享 | 你是否体验过Android手机插入耳机后仍外放的尴尬?
  • 提醒我喝水chrome插件开发指南
  • 一起来学SpringBoot | 第十篇:使用Spring Cache集成Redis
  • 带你开发类似Pokemon Go的AR游戏
  • 第二十章:异步和文件I/O.(二十三)
  • ​​快速排序(四)——挖坑法,前后指针法与非递归
  • (aiohttp-asyncio-FFmpeg-Docker-SRS)实现异步摄像头转码服务器
  • (转)fock函数详解
  • (转)GCC在C语言中内嵌汇编 asm __volatile__
  • (转)VC++中ondraw在什么时候调用的
  • (转)树状数组
  • .NET Core使用NPOI导出复杂,美观的Excel详解
  • .Net 代码性能 - (1)
  • .so文件(linux系统)
  • ;号自动换行
  • ??eclipse的安装配置问题!??
  • [ JavaScript ] JSON方法
  • [ vulhub漏洞复现篇 ] JBOSS AS 4.x以下反序列化远程代码执行漏洞CVE-2017-7504
  • [AIGC] CompletableFuture的重要方法有哪些?
  • [C++基础]-入门知识
  • [DAU-FI Net开源 | Dual Attention UNet+特征融合+Sobel和Canny等算子解决语义分割痛点]
  • [docker]docker网络-直接路由模式
  • [Docker]三.Docker 部署nginx,以及映射端口,挂载数据卷