[LeetCode] 第28场双周赛

P3

给你一个整数数组 arr 和一个整数值 target 。

请你在 arr 中找 两个互不重叠的子数组 且它们的和都等于 target 。可能会有多种方案,请你返回满足要求的两个子数组长度和的 最小值 。

请返回满足要求的最小长度和,如果无法找到这样的两个子数组,请返回 -1 。

解题思路:

  1. 我们会解一个子数组和的长度最小问题。
  2. 如何计算两个不想交子数组的长度和最小呢?假设,我们在i的位置发现从k到i的和满足条件,这时候如果我们知道位置k左侧满足条件的子数组长度是多少解决了到 i i i时的问题。那么,不难想到可以用dp来记录到到每个点的最小子数组长度。那么,问题也就解决了。
class Solution:
    def minSumOfLengths(self, arr: List[int], target: int) -> int:
        if len(arr) < 2: return -1
        n, inf = len(arr), len(arr)+1
        dp = [inf]*n
        d = collections.defaultdict(int)
        d[0],cur, ret=-1, 0, inf
        for i, x in enumerate(arr):
            cur += x
            dp[i] = min(dp[i], dp[i-1])
            if  cur - target in d:
                ai = i - d[cur - target]
                ret = min(ret, ai+dp[d[cur - target]])
                dp[i] = min(dp[i], ai)
            d[cur] = i
        return ret if ret < inf else -1

P4

给你一个房屋数组houses 和一个整数 k ,其中 houses[i] 是第 i 栋房子在一条街上的位置,现需要在这条街上安排 k 个邮筒。

请你返回每栋房子与离它最近的邮筒之间的距离的 最小 总和。

答案保证在 32 位有符号整数范围以内。

解题思路:

  1. 遇到这种题目先考虑最简单的case,固定 k = 1 k=1 k=1, 数组长度我们从1到10,观察数据。发现邮箱的位置总是数组的中位数。
  2. 考虑 k = 2 k=2 k=2的情况下,那么,我们需要两个邮箱,每个邮箱都会覆盖一定的范围。那么假设邮箱A覆盖了 0 , . . . , i 0,...,i 0,...,i的位置,那么邮箱B覆盖了 i + 1 , . . . , n − 1 i+1,...,n-1 i+1,...,n1个位置。那么,我们这是来考察邮箱的位置,如果它不在中位数的位置,那我们可以将它移到中位数的位置。同理,第二个邮箱。
  3. 考虑 k = i k=i k=i的情况, 这时候,我们关注最后一个邮箱的覆盖范围,可以是任意的,只要小于 n n n。所以,我们只能枚举。这时候,我们去掉了最后几个位置假设为 p + 1 , . . , n − 1 p+1,..,n-1 p+1,..,n1。那么前半部分就变成了 0 , . . , p 0,..,p 0,..,p,同时,我们用了 i − 1 i-1 i1个邮箱。也就是原问题的子问题。
from functools import lru_cache
class Solution:
    def minDistance(self, houses: List[int], K: int) -> int:
        if K >= len(houses): return 0
        A = sorted(houses)
        
        @lru_cache(None)
        def f(i,j):
            if i >= j: return 0
            return A[j]-A[i] + f(i+1, j-1)
        
        inf = 10**6
        dp = [[inf]*(K+1) for _ in range(len(A))]
        
        ## initial
        for i in range(len(A)):
            dp[i][1] = f(0,i)
        
        for k in range(2, K+1):
            for i in range(len(A)):
                for p in range(i+1):
                    dp[i][k] = min(dp[i][k],dp[p][k-1] + f(p+1, i))
        return dp[-1][-1]
        
        

个人小结

这次竞赛,有点不习惯,第三题出了dp,第四题还是dp。而且,第三题,做的时候,计算找k那步,符号搞错了。下次,应该在纸上把公式写清楚。第四题,第一反应,第三题出了dp了,以为不是,看着觉得像k-means聚类,试了一下,果然不是leetcode考察范围。一开始也没有仔细分析,赛后想了一下还是很简单的。这次又要掉分了。 发挥的不太好。

你可能感兴趣的:(python3,leetcode周赛)