Leetcode 3049. Earliest Second to Mark Indices II

  • [Leetcode 3049. Earliest Second to Mark Indices II](#Leetcode 3049. Earliest Second to Mark Indices II)
    • [1. 解题思路](#1. 解题思路)
    • [2. 代码实现](#2. 代码实现)
    • [3. 算法优化](#3. 算法优化)

1. 解题思路

这道题我看貌似难度报表,比赛的时候貌似只有36个人搞定了这道题目,然后最快的人耗时也花了40min以上,就很离谱,和平时基本15分钟就能有大佬全部做出来4道题的情况完全不懂,就很吓人。

所以基本上我对这道题也算是一个半放弃的状态,不过后来还是看了一下,然后发现其实也不是完全无法上手,本质上还是求一个处理的最优解,且显然这个操作依然是单调的,即从某一个位置开始,后续所有的操作序列都可以使得所有的位置都被mark上。

因此,最开始我的思路也是二分法来求任意一个位置作为操作序列的结束点时能否判断其是否可以使得所有的位置都被mark上,然后就和上一题一样,这里就变成了一个贪婪算法,要最优的分配点数使得所有的值都可以先降为0然后再mark上。

但是比较尴尬的是这里我们的贪婪算法并没有完成,因此有以下一个判断无法彻底想明白:

  • 已知任何一次直接归零操作都必须附加一次mark操作进行完成,因此对于任意一个点,我们需要判断是否要等待后续直接操作位然后再多加一轮操作使之mark还是直接使用当前富裕的点数(如果有的话)进行归零然后mark。

因此后续干脆就是直接绕开这个,走了最暴力的动态规划的方法,遍历了所有的可能,即每一个位置到底是走的快速清零还是进行-1或者mark操作,这里,显然如果要进行快速清零那必然是在某个位置在changeIndices当中最早出现的位置上。

但是,直接地实现会出现内存爆炸的情况,因此,我们做了一些不严谨的剪枝,即如果某次快速清零可以省出100以上的富裕,那么必走快速清零,反之考虑是否忽略掉这个快速清零的路线。

需要强调,这个方式是不严谨的,因为如果存在全面富裕了非常多的点数远大于100,而最后来一个可以清空100的操作,那不一定要走快速清零的,因为那样会需要额外多一次操作。

但是对于绝大多数的情况上述结果是对的,在这里也确实可以通过测试样例。

2. 代码实现

给出python代码实现如下:

python 复制代码
class Solution:
    def earliestSecondToMarkIndices(self, nums: List[int], changeIndices: List[int]) -> int:
        n, m = len(nums), len(changeIndices)
        changeIndices = [x-1 for x in changeIndices]
        needed = sum(nums) + n
        first_seen = {}
        for i, x in enumerate(changeIndices):
            if x not in first_seen and nums[x] > 1:
                first_seen[x] = i
        first_seen = {v:k for k, v in first_seen.items()}

        @lru_cache(None)
        def dp(idx, need, extra):
            if need <= 1 and extra <= 1:
                return idx
            elif idx >= m:
                return m
            if idx not in first_seen:
                return dp(idx+1, need-1, max(extra-1, 0))
            i = first_seen[idx]
            if nums[i] >= 100:
                return dp(idx+1, need-nums[i], extra+1)
            else:
                return min(dp(idx+1, need-1, max(extra-1, 0)), dp(idx+1, need-nums[i], extra+1))
            
        ans = dp(0, needed, 0)
        return ans+1 if ans != m else -1

提交代码评测得到:耗时1219ms,占用内存288.9MB。

3. 算法优化

因为前面也反复强调了,上述解法有效,但是剪枝的存在使得上述解法并不严谨,因此,我们在这里摘录一下其他大佬们的解法作为补充,有兴趣的读者可以自行研读学习一下,这里就让我偷个懒了......

python 复制代码
class Solution:
    def earliestSecondToMarkIndices(self, nums: List[int], changeIndices: List[int]) -> int:
        first_idx = [-1] * len(nums)
        for i in range(len(changeIndices)):
            if first_idx[changeIndices[i]-1] == -1:
                first_idx[changeIndices[i]-1] = i

        for i, num in enumerate(nums):
            if num <= 1:
                first_idx[i] = -1

        default_needs = sum(nums) + len(nums)

        def is_possible(k):
            left = 0
            h = []

            pos_count = 0
            flag = False
            for j in range(k, -1, -1):
                if flag:
                    pos_count += 1
                    flag = False
                else:
                    flag = True

                left += 1

                i1 = first_idx[changeIndices[j]-1]
                if i1 == j:
                    heapq.heappush(h, [nums[changeIndices[j]-1] - 1, changeIndices[j]-1])
                if len(h) > pos_count:
                    heapq.heappop(h)

            return default_needs - sum(h1[0] for h1 in h) <= left

            ans = 0
            return ans

        l, r = len(nums)-1, len(changeIndices)-1
        while l < r:
            m = (l + r) // 2
            if is_possible(m):
                r = m
            else:
                l = m + 1
        if l > r or not is_possible(l):
            return -1
        return l+1
相关推荐
闻缺陷则喜何志丹1 天前
【计算几何 最短路 动态规划】P1354 房间最短路问题
数学·算法·动态规划·最短路·计算几何·洛谷
LYFlied2 天前
【每日算法】LeetCode 1143. 最长公共子序列
前端·算法·leetcode·职场和发展·动态规划
长安er2 天前
LeetCode 20/155/394/739/84/42/单调栈核心原理与经典题型全解析
数据结构·算法·leetcode·动态规划·
LYFlied2 天前
【每日算法】LeetCode 5. 最长回文子串(动态规划)
数据结构·算法·leetcode·职场和发展·动态规划
Espresso Macchiato3 天前
Leetcode 3791. Number of Balanced Integers in a Range
leetcode hard·leetcode周赛482·leetcode 3791
Espresso Macchiato3 天前
Leetcode 3782. Last Remaining Integer After Alternating Deletion Operations
迭代·leetcode hard·leetcode双周赛172·leetcode 3782
qq_433554543 天前
C++区间DP
c++·算法·动态规划
Espresso Macchiato3 天前
Leetcode 3768. Minimum Inversion Count in Subarrays of Fixed Length
滑动窗口·leetcode hard·leetcode双周赛171·leetcode 3768
Espresso Macchiato3 天前
Leetcode 3785. Minimum Swaps to Avoid Forbidden Values
leetcode hard·leetcode周赛481·leetcode 3785
Wuliwuliii3 天前
闵可夫斯基和、需存储的最小状态集
c++·算法·动态规划·闵可夫斯基和