Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Leetcode 2646. Minimize the Total Price of the Trips #242

Open
Woodyiiiiiii opened this issue Apr 20, 2023 · 0 comments
Open

Leetcode 2646. Minimize the Total Price of the Trips #242

Woodyiiiiiii opened this issue Apr 20, 2023 · 0 comments

Comments

@Woodyiiiiiii
Copy link
Owner

Woodyiiiiiii commented Apr 20, 2023

2646. Minimize the Total Price of the Trips

好题。综合了很多思想。

首先看清题意。题目要求的是在第一次操作前就要考虑是否对结点价值减半了,同时要求选择减半的结点不相邻。那么这里我陷入了思维误区,以为贪心求两种分类就够了(从0结点选择或不选择开始分成两类),其实很明显错误了,因为每个结点的价值不同,可能会出现选择减半的结点距离很远的情况。那这种就想到了337. House Robber III的思想,也就是树形DP

显然这种non-adjacent的题型都是用House Robber的DP思路。

又因为可以观察到对每个trip来说,路径都是固定的(因为是树),同时结点之间关联不大。所以接下来我们根据结点数量少的限制,可以对每个trip跑一遍DFS,记录路径经过的结点次数,最后汇总,DP计算总价值。

class Solution {
    Map<Integer, Set<Integer>> graph;
    int[] price;
    int[] count;
    List<Integer> routine;

    public int minimumTotalPrice(int n, int[][] edges, int[] price, int[][] trips) {
        this.graph = new HashMap<>();
        for (int i = 0; i < n; i++) {
            graph.put(i, new HashSet<>());
        }
        for (int[] edge : edges) {
            graph.get(edge[0]).add(edge[1]);
            graph.get(edge[1]).add(edge[0]);
        }
        this.price = price;

        count = new int[n];
        for (int[] trip : trips) {
            routine = new ArrayList<>();
            List<Integer> path = new ArrayList<>();
            path.add(trip[0]);
            findPath(-1, trip[0], trip[1], path);
            for (int i : routine) {
                count[i]++;
            }
        }

        int[] res = dfs(0, -1);
        return Math.min(res[0], res[1]);
    }

    private int[] dfs(int cur, int pre) {
        int[] res = new int[2];
        int notHalve = price[cur] * count[cur];
        int halve = notHalve / 2;
        for (int next : graph.get(cur)) {
            if (next == pre) {
                continue;
            }
            int[] nextRes = dfs(next, cur);
            halve += nextRes[0];
            notHalve += Math.min(nextRes[0], nextRes[1]);
        }
        res[0] = notHalve;
        res[1] = halve;
        return res;
    }

    private void findPath(int pre, int cur, int end, List<Integer> path) {
        if (cur == end) {
            routine = new ArrayList<>(path);
            return;
        }
        for (int next : graph.get(cur)) {
            if (next == pre) {
                continue;
            }
            path.add(next);
            findPath(cur, next, end, path);
            path.remove(path.size() - 1);
        }
    }
}

时间复杂度O(nm),其中m是trip长度;空间复杂度O(n)。

第二种方法是用Tarjan 离线 LCA + 树上差分,太难了看详解。

相关联的题目:

337. House Robber III

下面是我之前的做法,深度后序遍历,其中创建Map缓存防止重复子问题出现。

class Solution {
    
    Map<TreeNode, Integer> memo = new HashMap<>();
    
    public int rob(TreeNode root) {
        if (root == null) {
            return 0;
        }
        if (memo.containsKey(root)) {
            return memo.get(root);
        }
        
        int l = rob(root.left);
        int r = rob(root.right);
        int ll = root.left == null ? 0 : rob(root.left.left);
        int lr = root.left == null ? 0 : rob(root.left.right);
        int rr = root.right == null ? 0 : rob(root.right.right);
        int rl = root.right == null ? 0 : rob(root.right.left);
        
        int max = Math.max(l + r, root.val + ll + lr + rr + rl);
        
        memo.put(root, max);
        
        return max;
        
    }
}

其中发现,每个结点最后的值只跟他的孩子结点有关,所以我们可以设计后序树形DP,这样省去了缓存。

class Solution {
    public int rob(TreeNode root) {
        int[] res = dfs(root);
        return Math.max(res[0], res[1]);
    }

    private int[] dfs(TreeNode root) {
        int[] res = new int[2];
        if (root == null) {
            return res;
        }
        res[0] = root.val;
        int[] left = dfs(root.left);
        int[] right = dfs(root.right);
        res[1] += Math.max(left[0], left[1]) + Math.max(right[0], right[1]);
        res[0] += (left[1] + right[1]);
        return res;
    }
}

相关资料

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant