Woodyiiiiiii / LeetCode

My private record of Leetcode solution

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Leetcode 2830. Maximize the Profit as the Salesman

Woodyiiiiiii opened this issue · comments

[2830. Maximize the Profit as the Salesman

2830. Maximize the Profit as the Salesman

类似题目:
2008. Maximum Earnings From Taxi

二分法方法题单(不同的是值域范围很大):
2054. Two Best Non-Overlapping Events / 1235. Maximum Profit in Job Scheduling

一、二分法

一看到题目我就知道要用选或不选DP,但是因为有两个范围,很容易达到范围相乘的效果造成超时。那么问题关键是遍历一个变量,对另一个变量模糊处理。竞赛中我理所当然地把能购买的房子变量当作主遍历变量,对值域进行模糊处理了。模糊处理的方法是用二分法,如果选择卖了该房子,那么就跳跃到该房子卖后所能选择的最近的卖房值域起点。

No.2054/No.1235一样的思路,在值域范围很大的情况下优先使用。

class Solution {
    List<List<Integer>> offers;
    int n;
    int offerSize;
    int[] dp;
    TreeMap<Integer, Integer> treeMap;

    public int maximizeTheProfit(int n, List<List<Integer>> offers) {
        offers.sort((o1, o2) -> {
            if (o1.get(0).equals(o2.get(0)))
                return o1.get(1).compareTo(o2.get(1));
            return o1.get(0).compareTo(o2.get(0));
        });
        this.offers = offers;
        this.n = n;
        this.offerSize = offers.size();
        this.dp = new int[offerSize];
        Arrays.fill(dp, -1);
        treeMap = new TreeMap<>();
        for (int i = 0; i < offerSize; i++) {
            int start = offers.get(i).get(0);
            if (!treeMap.containsKey(start)) {
                treeMap.put(start, i);
            }
        }
        treeMap.put(n, offerSize);
        return dfs(0);
    }

    private int dfs(int i) {
        if (i >= offerSize) {
            return 0;
        }
        if (dp[i] != -1) {
            return dp[i];
        }

        int max = dfs(i + 1);
        List<Integer> offer = offers.get(i);
        int end = offer.get(1), gold = offer.get(2);
        Integer next = treeMap.ceilingKey(end + 1);
        if (next != null) {
            int j = treeMap.get(next);
            max = Math.max(max, dfs(j) + gold);
        }

        return dp[i] = max;
    }
}

值域

该方法注重值域,适用于值域没有超过极限的情况下。对值域遍历,每次遍历该点所能选择后的房子范围。dp[i]表示[0,i]之间的最大利润。时间复杂度是O(n + m)。

类似题目是No.2008。

class Solution {
    public int maximizeTheProfit(int n, List<List<Integer>> offers) {
        Map<Integer, Set<int[]>> map = new HashMap<>();
        for (List<Integer> offer : offers) {
            int start = offer.get(0), end = offer.get(1), profit = offer.get(2);
            map.putIfAbsent(end, new HashSet<>());
            map.get(end).add(new int[]{start, profit});
        }
        int[] dp = new int[n + 1];
        for (int i = 0; i < n; i++) {
            dp[i + 1] = dp[i];
            for (int[] offer : map.getOrDefault(i, new HashSet<>())) {
                dp[i + 1] = Math.max(dp[i + 1], dp[offer[0]] + offer[1]);
            }
        }
        return dp[n];
    }
}

总结

两个方法侧重点不同,但原理都是从满足时间复杂度出发考虑。第一种二分法更适用。