【算法思考记录】力扣2646. 最小化旅行的价格总和【C++,树上DP,深度优先搜索】

原题链接

题目概述

本题是一个关于树形数据结构的算法问题,其中涉及无向、无根树的遍历、路径选择以及成本优化。我们需要在给定的树结构中,找到在特定规则下的最小旅行成本。

题目细节

  • 树的构造: 由 n 个节点组成,编号从 0n - 1,通过给定的 edges 数组定义节点之间的连接关系。
  • 节点价格: 每个节点都有一个价格,由 price 数组给出。
  • 旅行规划: trips 数组中包含多次旅行的起始和结束节点。
  • 成本优化: 在旅行前,可以选择一些非相邻节点减半其价格。

示例分析

示例 1 提供了一种减半选择,从而达到最小总成本 23。这要求我们不仅计算原始路径成本,还要考虑减半优化的可能性。

解题思路

  1. 最短路径算法: 首先计算在不考虑价格减半的情况下,每次旅行的最低价格总和。这可以通过Dijkstra算法实现,但是如果考虑价格减半的情况,Dijkstra就不一定可以解决问题了,因此,我们先统计每个节点的经过次数,这点可以直接使用dfs实现,代码中实现了dfs_calc_cnt这个函数来完成该任务。
  2. 价格减半策略: 考虑价格减半,我们应该选择减少那些能节省最多费用的节点,由于不能给相邻的节点同时进行价格减半,因此我们需要使用动态规划法,实现在较低复杂度下穷举每个节点的减半和不减半的情况下产生的费用。

C++代码实现

class Solution {
public:
    inline bool dfs_calc_cnt(int node, int fa, int end, vector<int> &cnt, const vector<vector<int>> &graph) {
        if (node == end) { 
          cnt[node]++; 
          return true; 
        }// 找到一条合法路径
        
        for (int chi : graph[node]) {
          if (chi == fa) continue;
          // 如果递归子树返回true,代表可以到达end,是合法的路径。
          bool is_right_way = dfs_calc_cnt(chi, node, end, cnt, graph);
          if (is_right_way) {
            cnt[node]++;
            return true;
          }
        }
        return false;
    }

    int minimumTotalPrice(int n, vector<vector<int>>& edges, vector<int>& price, vector<vector<int>>& trips) {
        vector<vector<int>> graph(n);
        for (auto &edge : edges) {
          int x = edge[0], y = edge[1];
          graph[x].push_back(y);
          graph[y].push_back(x);
        }

        vector<int> cnt(n); // 记录各个节点的经过次数
        for (auto &trip : trips) {
          int start = trip[0], end = trip[1];
          dfs_calc_cnt(start, -1, end, cnt, graph);
        }

        function<pair<int, int>(int, int)> dfs = [&](int node, int fa) -> pair<int, int> {
          int not_half = price[node] * cnt[node];
          int half = not_half / 2;
          for (auto &chi : graph[node]) {
            if (chi == fa) continue;
            auto [chi_not_half, chi_half] = dfs(chi, node);
            not_half += min(chi_not_half, chi_half);
            half += chi_not_half; // 因为本节点减半,所以下一个子节点不可减半
          }
          return {not_half, half};
        };

        auto [not_half, half] = dfs(0, -1);
        return min(not_half, half);



    }
};

代码解析

  • 建立图结构: 使用 vector<vector<int>> 来表示图的邻接表。
  • 统计经过次数: 使用深度优先搜索(DFS)统计每个节点在所有旅行中的经过次数。
  • 成本计算: 分别计算每个节点在减半和不减半情况下的成本,并递归地计算整棵树的最小成本。

总结

此问题的解决方案不仅要求熟练掌握树的遍历算法,还要对动态规划有深入的理解。本文通过详细解释并展示C++代码实现,提供了一个清晰的解题路径。


注意: 本博客旨在提供算法问题的解决思路和示例代码,实际应用时需根据具体情况进行调整。

  • 15
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值