2646.最小化旅行的价格总和
链接:2646.最小化旅行的价格总和
难度:Hard
标签:树、深度优先搜索、图、数组、动态规划
简介:现有一棵无向、无根的树,树中有 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 - python
- 编辑时间:2023-12-06
- 执行用时:888ms
- 内存消耗:257.3MB
- 编程语言:python
- 解法介绍:提前统计每个点会被经过的次数,然后dp判断每个点打折和不打折的情况。
class Solution:
        def minimumTotalPrice(self, n: int, edges: List[List[int]], price: List[int], trips: List[List[int]]) -> int:
            nodes = [[] for _ in range(n)]
            for n1, n2 in edges:
                nodes[n1].append(n2)
                nodes[n2].append(n1)
            cnts = [0] * n
            def dfs(start: int, end: int, used: int) -> bool:
                if start == end:
                    cnts[end] += 1
                    return True
                check = False
                for next in nodes[start]:
                    if (used & (1 << next)) == 0 and dfs(next, end, used | (1 << next)):
                        cnts[start] += 1
                        check = True
                return check
            for start, end in trips: dfs(start, end, 1 << start)
            sums = sum(c * price[i] for i, c in enumerate(cnts))
            @cache
            def try_trip(idx: int, used: int, can: bool) -> int:
                res1 = 0
                if can: 
                    res1 += int(cnts[idx] * price[idx] / 2)
                    for next in nodes[idx]:
                        if (used & (1 << next)) == 0:
                            res1 += try_trip(next, used | (1 << next), not can)
                res2 = 0
                for next in nodes[idx]:
                    if (used & (1 << next)) == 0:
                        res2 += try_trip(next, used | (1 << next), True)
                return max(res1, res2)
            return min(sums - try_trip(i, 1 << i, True) for i in range(n))
题解 2 - cpp
- 编辑时间:2023-04-16
- 执行用时:760ms
- 内存消耗:241.1MB
- 编程语言:cpp
- 解法介绍:树dp,记录选当前点和不选时的打折价格。
#define pii pair<int, int>
    #define X first
    #define Y second
    struct Node {
        int idx, price;
        vector<int> next;
    };
    struct QNode {
        int i, sum;
        vector<int> list;
    };
    class Solution {
    public:
        int minimumTotalPrice(int n, vector<vector<int>>& edges, vector<int>& price, vector<vector<int>>& trips) {
            vector<Node> list(n);
            for (int i = 0; i < n; i++) {
                list[i].idx = i;
                list[i].price = price[i];
            }
            for (auto &edge : edges) {
                list[edge[0]].next.push_back(edge[1]);
                list[edge[1]].next.push_back(edge[0]);
            }
            // 记录所有路径
            vector<vector<QNode>> roads(n, vector<QNode>(n));
            for (int i = 0; i < n; i++) {
                roads[i][i] = QNode{ i, list[i].price, vector<int>(1, i)};
                queue<QNode> q;
                q.push(QNode{ i, list[i].price, vector<int>(1, i)});
                unordered_set<int> used;
                used.insert(i);
                while (q.size()) {
                    auto cur = q.front();
                    q.pop();
                    for (auto &next : list[cur.i].next) {
                        if (used.count(next)) continue;
                        used.insert(next);
                        auto nextNode = cur;
                        nextNode.i = next;
                        nextNode.sum += list[next].price;
                        nextNode.list.push_back(next);
                        roads[i][next] = nextNode;
                        q.push(nextNode);
                    }
                }
            }
            // 记录不打折时总价,和每个点会被遍历几次
            int sums = 0, res = 0x7fffffff;
            vector<int> weights(n, 0);
            for (auto &trip : trips) {
                sums += roads[trip[0]][trip[1]].sum;
                for (auto &item : roads[trip[0]][trip[1]].list) {
                    weights[item]++;
                }
            }
            // X 记录这个点选的时候最多能打多少
            // Y 记录这个点不选的时候最多能打多少
            unordered_set<int> used;
            function<pii(int)> discount = [&](int start) -> pii {
                pii res = make_pair(list[start].price / 2 * weights[start], 0);
                for (auto &next : list[start].next) {
                    if (used.count(next)) continue;
                    used.insert(next);
                    auto nextRes = discount(next);
                    res.X += nextRes.Y;                
                    res.Y += max(nextRes.X, nextRes.Y);
                    used.erase(next);
                }
                return res;
            };
            used.insert(0);
            auto disres = discount(0);
            res = min(res, sums - max(disres.X, disres.Y));
            used.erase(0);
            return res;
        }
    };