Leetcode 2646. 最小化旅行的价格总和(暴力 DFS + 树形 DP)

news/2024/7/20 21:49:58 标签: 深度优先, leetcode, 算法
  • Leetcode 2646. 最小化旅行的价格总和(暴力 DFS + 树形 DP)
  • 题目
    • 现有一棵无向、无根的树,树中有 n 个节点,按从 0 到 n - 1 编号。给你一个整数 n 和一个长度为 n - 1 的二维整数数组 edges ,其中 edges[i] = [ai, bi] 表示树中节点 ai 和 bi 之间存在一条边。
    • 每个节点都关联一个价格。给你一个整数数组 price ,其中 price[i] 是第 i 个节点的价格。
    • 给定路径的 价格总和 是该路径上所有节点的价格之和。
    • 另给你一个二维整数数组 trips ,其中 trips[i] = [starti, endi] 表示您从节点 starti 开始第 i 次旅行,并通过任何你喜欢的路径前往节点 endi 。
    • 在执行第一次旅行之前,你可以选择一些 非相邻节点 并将价格减半。
    • 返回执行所有旅行的最小价格总和。
    • 1 <= n <= 50
    • edges.length == n - 1
    • 0 <= ai, bi <= n - 1
    • edges 表示一棵有效的树
    • price.length == n
    • price[i] 是一个偶数
    • 1 <= price[i] <= 1000
    • 1 <= trips.length <= 100
    • 0 <= starti, endi <= n - 1
  • 解法
    • 暴力 DFS + 树形 DP:
    • 第 1 步:
    • 思考在不折半的情况下,每次旅行的最小值:start 到 end 的最短路径(不走回头路)
    • 第 2 步:
    • 先建树,然后枚举 trips 计算每次 start 到 end 的最短路径
    • 找到该路径所有点,求出每个点使用 次数 * 价格
    • 此时问题转化为:每个点的价格固定(次数 * 价格),求 非相邻节点 后、所有节点总和的最小值
    • 第 3 步:
    • 类似:337. 打家劫舍 III
    • 树形 DP
    • dp[i][j] 代表 i 子树处、所有旅行的最小价格总和,j=0-不折半,j=1-i折半
    • 动规转移方程:
      • dp[i][0] = min(dp[child][0],dp[child][1]) + pointPrice[i]:i 不折半则 i 的孩子可以折半也可以不折半
      • dp[i][1] = dp[child][0] + pointPrice[i]/2:i 折半则 i 的孩子一定不折半
    • 时间复杂度:O(n * m),空间复杂度:O(n)
  • 代码
/**
     * 暴力 DFS + 树形 DP:
     *
     * 第 1 步:
     * 思考在不折半的情况下,每次旅行的最小值:start 到 end 的最短路径(不走回头路)
     *
     * 第 2 步:
     * 先建树,然后枚举 trips 计算每次 start 到 end 的最短路径
     * 找到该路径所有点,求出每个点使用 次数 * 价格
     * 此时问题转化为:每个点的价格固定(次数 * 价格),求 非相邻节点 后、所有节点总和的最小值
     *
     * 第 3 步:
     * 类似:337. 打家劫舍 III
     * 树形 DP
     * dp[i][j] 代表 i 子树处、所有旅行的最小价格总和,j=0-不折半,j=1-i折半
     * 动规转移方程:
     *     * dp[i][0] = min(dp[child][0],dp[child][1]) + pointPrice[i],i 不折半则 i 的孩子可以折半也可以不折半,
     *     * dp[i][1] = dp[child][0] + pointPrice[i]/2,i 折半则 i 的孩子一定不折半,
     * 时间复杂度:O(n * m),空间复杂度:O(n)
     */
    public int minimumTotalPrice(int n, int[][] edges, int[] price, int[][] trips) {
        // 建树
        List<Integer>[] treeList = buildTree(n, edges);

        // 每个点使用次数 * 价格
        int[] pointPrice = new int[n];

        // 枚举 trips 计算每次 start 到 end 的最短路径(不走回头路)
        List<Integer> pointList = new ArrayList<>();
        for (int[] trip : trips) {
            int start = trip[0];
            int end = trip[1];

            pointList.clear();
            pointList.add(-1);
            dfsShortestPath(start, -1, treeList, end, pointList);

            // 求出每个点使用次数 * 价格
            for (int i = 1; i < pointList.size(); i++) {
                int point = pointList.get(i);
                pointPrice[point] += price[point];
            }
        }

        // dp[i][j] 代表 i 子树处、所有旅行的最小价格总和,j=0-不折半,j=1-i折半
        int[][] dp = new int[n][2];

        dfsMinPrice(0, -1, treeList, pointPrice, dp);


        return Math.min(dp[0][0], dp[0][1]);

    }

    /**
     * dp[i][j] 代表 i 子树处、所有旅行的最小价格总和,j=0-不折半,j=1-i折半
     */
    private void dfsMinPrice(int son, int father, List<Integer>[] treeList, int[] pointPrice, int[][] dp) {

        int nextNot = 0;
        int nextMin = 0;
        for (int next : treeList[son]) {
            if (next != father) {

                dfsMinPrice(next, son, treeList, pointPrice, dp);
                nextNot += dp[next][0];
                nextMin += Math.min(dp[next][0], dp[next][1]);
            }
        }

        dp[son][0] = nextMin + pointPrice[son];
        dp[son][1] = nextNot + (pointPrice[son] >> 1);

    }

    /**
     * 计算每次 start 到 end 的最短路径(不走回头路)
     * 返回路径上所有的点
     */
    private void dfsShortestPath(int son, int father, List<Integer>[] treeList, int end, List<Integer> pointList) {
        if (son == end) {
            pointList.add(son);
            return;
        }

        for (int next : treeList[son]) {
            if (next != father) {
                pointList.add(son);

                dfsShortestPath(next, son, treeList, end, pointList);

                // 遍历到 end 节点,不用清理只直接退出
                if (pointList.get(pointList.size() - 1) == end) {
                    return;
                }

                pointList.remove(pointList.size() - 1);
            }
        }

    }

    /**
     * 建树
     */
    private List<Integer>[] buildTree(int n, int[][] edges) {
        List<Integer>[] edgeList = new ArrayList[n];
        for (int i = 0; i < n; i++) {
            edgeList[i] = new ArrayList<>();
        }

        for (int i = 0; i < edges.length; i++) {
            int u = edges[i][0];
            int v = edges[i][1];
            edgeList[u].add(v);
            edgeList[v].add(u);
        }
        return edgeList;
    }

http://www.niftyadmin.cn/n/5247572.html

相关文章

go-zero 开发入门-加法客服端示例

定义 RPC 接口文件 接口文件 add.proto 的内容如下&#xff1a; syntax "proto3"; package add;// 当 protoc-gen-go 版本大于 1.4.0 时需加上 go_package&#xff0c;否则编译报错“unable to determine Go import path for” option go_package "./add&qu…

Stable Diffusion XL on diffusers

Stable Diffusion XL on diffusers 翻译自&#xff1a;https://huggingface.co/docs/diffusers/using-diffusers/sdxl v0.24.0 非逐字翻译 Stable Diffusion XL (SDXL) 是一个强大的图像生成模型&#xff0c;其在上一代 Stable Diffusion 的基础上主要做了如下优化&#xff1a;…

VUE学习一、环境的安装

1.node.js安装 node.js是前端依赖的环境, 类似于java中的jdk 下载地址 node.js 下载 msi文件 下完就是一顿嘎嘎安装 , 安装后可以cmd看看node和npm的版本 1.2 yarn的安装 Yarn是Facebook最近发布的一款依赖包安装工具。Yarn是一个新的快速安全可信赖的可以替代NPM的依赖管…

AI发展下服务器的选择非常重要

在AI发展下&#xff0c;服务器的选择非常重要。以下是一些选择服务器时需要考虑的因素&#xff1a; 1. 计算能力&#xff1a;AI需要大量的计算资源来进行训练和推理。因此&#xff0c;选择具有强大计算能力的服务器是至关重要的。 2. 内存容量&#xff1a;AI需要大量的内存来…

sfp8472学习CDR

1,cdr名称解释 因为光信号传输至一定距离的时候,通常是长距离传输,其波形会出现一定程度的失真,接收端接收到的信号是一个个长短不一的脉冲信号,这个时候在接收端,我们就无法得到我们需要的数据。所以,这个时候就需要有信号的再生,信号的再生功能为再放大、再整形和再…

ubuntu22下使用nvidia 2080T显卡部署pytorch

1.直接到NVIDA官网下载相应的驱动&#xff0c;然后安装官方驱动 | NVIDIA 2.下载相应版本cuda&#xff0c;并安装&#xff0c;安装时不安装驱动 3.conda install pytorch2.1.0 torchvision0.16.0 torchaudio2.1.0 pytorch-cuda12.1 -c pytorch -c nvidia 安装pytorch。 安装…

MyBatis中的N+1问题,使用ResultSet来解决,需要存储过程【非常详细】

基础表sql 订单表 CREATE TABLE test_order (order_id bigint(20) NOT NULL AUTO_INCREMENT COMMENT 订单id,order_name varchar(255) NOT NULL DEFAULT COMMENT 订单名字,PRIMARY KEY (order_id) ) ENGINEInnoDB AUTO_INCREMENT3 DEFAULT CHARSETutf8mb4 COMMENT订单表;INS…

融云 Global IM UIKit,灵活易用的即时通讯组件设计思路和最佳实践

&#xff08;全网都在找的《社交泛娱乐出海作战地图》&#xff0c;点击获取&#x1f446;&#xff09; 融云近期推出的 Global IM UIKit&#xff0c;支持开发者高效满足海外用户交互体验需求&#xff0c;且保留了相当的产品张力赋予开发者更多自由和灵活性&#xff0c;是实现全…