Leetcode 3505. Minimum Operations to Make Elements Within K Subarrays Equal

1. 解题思路

这一题大的思路上不难想到就是一个动态规划的思路。我们分别考察每一个位置是否需要作为某一个子串的开始位置,然后考察对应子串要使之变为完全相同所需要的最小操作数,然后找出其总和的最小值即可。其总体的算法复杂度在不考虑求子串的最小操作数的情况下就会是 O ( N K ) O(NK) O(NK)

但是,这复杂度还是很高的,因此,也就要求我们事实上对于上述子问题,即给定一个长度为x的数组,求令其变得完全相同时所需的最小操作数,的算法复杂度要求就会非常高,至少不是一个滑动窗口遍历的 O ( X ) O(X) O(X)可以处理的,我们需要将其压缩至 O ( l o g X ) O(logX) O(logX)甚至 O ( 1 ) O(1) O(1)

万幸的是,通过数学分析,我们可以知道对于一个给定一个长度为x的数组,其所需的最小操作数一定就是将其全部变为其中位数时所需的操作数。而基于此,我们可以提前先算出每一个位置作为起始位置时其长度为x的数组的最小操作数,此时我们可以通过前一个位置的结果进行调整,两者变动的元素至多只有一个,因此我们可以复用之前的结果从而省略掉排序的过程,整体的算法复杂度就会是 O ( N l o g X ) O(NlogX) O(NlogX)

此时,总的题目的算法复杂度就会是 O ( N K + N l o g X ) O(NK + NlogX) O(NK+NlogX),整体就还勉强在允许范围内了。

2. 代码实现

给出python代码实现如下:

class Solution:
    def minOperations(self, nums: List[int], x: int, k: int) -> int:
        n = len(nums)

        min_ops = [math.inf for i in range(n-x+1)]
        sub = sorted(nums[:x])
        m = (x+1) // 2
        left, ls = sub[:m], sum(sub[:m])
        right, rs = sub[m:], sum(sub[m:])
        min_ops[0] = (m*2-x) * left[-1]  - ls + rs
        for i in range(n-x):
            if bisect.bisect_left(left, nums[i]) < m:
                left.pop(bisect.bisect_left(left, nums[i]))
                ls -= nums[i]
            else:
                right.pop(bisect.bisect_left(right, nums[i]))
                rs -= nums[i]
            
            bisect.insort(left, nums[i+x])
            ls += nums[i+x]
            
            while len(left) < m:
                elem = right.pop(0)
                rs -= elem
                bisect.insort(left, elem)
                ls += elem
            while len(left) > m:
                elem = left.pop(-1)
                ls -= elem
                bisect.insort(right, elem)
                rs += elem
            while left[-1] > right[0]:
                l, r = left.pop(-1), right.pop(0)
                bisect.insort(left, r)
                bisect.insort(right, l)
                ls = ls - l + r
                rs = rs - r + l
            min_ops[i+1] = (m*2-x) * left[-1]  - ls + rs
        
        @lru_cache(maxsize = 10000)
        def dp(idx, k):
            if k == 0:
                return 0
            if idx >= n:
                return math.inf 
            if idx+k*x > n:
                return math.inf
            elif idx+k*x == n:
                return min_ops[idx] + dp(idx+x, k-1)
            
            return min(dp(idx+1, k), min_ops[idx] + dp(idx+x, k-1))
        
        return dp(0, k)

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值