Leetcode410 分割数组的最大值/探索卡片/学渣のday1

分割数组的最大值

好了,这道题看了2min,一点思路没得,喳喳诚实地打开了百度开始搜索题解。
磨蹭,发呆,喝水,逛blog,约20min后,继续回来写。
这个题网上有两种思路,一种是二分,一种是DP,先来学习我最讨厌的DP。

DP

令dp[i][j]表示把前j个数分为i组的i个和的最大值的最小值(就是题目要求的那个)。
那么子问题表示为min(max(dp[i-1][k],sum(nums[j-k:j]))) ,其中k从i-1取到j-1。

class Solution:
    def splitArray(self, nums: List[int], m: int) -> int:
        n = len(nums)
        dp = [[10000000]*(n+1) for _ in range(m+1)]
        maxArr=[0]*(n+1)
        for i in range(n):
            maxArr[i+1] = maxArr[i]+nums[i]
        for i in range(m+1):
            for j in range(n+1):
                dp[i][j] = maxArr[-1]
        dp[0][0]=0
        for i in range(1,m+1):
            for j in range(1,n+1):
                for k in range(i-1,j):
                    value = max(dp[i-1][k],maxArr[j]-maxArr[k])
                    if value<dp[i][j]:
                    	dp[i][j]=value
        return dp[m][n]

尝试了一会,发现自己理解错了,于是可耻地看了大佬的代码,改写成了python。
错误的就不贴了,分析一下这个ac的dp代码,首先速度巨慢,10000ms (orz。。)
首先是定义dp数组,然后是定义一个maxArr数组,表示nums前i个数之和,这个在后面计算sum(nums[i:j])时管用,只要减一下就行了。
  然后初始化dp数组,之前自己在这儿出错了,这里将所有位置都初始化为maxArr[-1],也就是nums的和。然后是dp[0][0]=0
开始dp过程,都是从0开始.这里其实可以小改一下,i和j都从1开始,因为分成0组或者0个数分组都是不存在的情况,改动后从10000ms降到了7000ms(???)。然后k的取值很简单,因为这里只考虑分成i-1组,那么至少要有i-1个数,最多j-1个数,不能到j,否则第j组就0个数了,不存在的。
比较的代码:
     value = max(dp[i-1][k],maxArr[j]-maxArr[k])
     dp[i][j] = min(dp[i][j],value)
  这里之前理解也有点小偏差。。应该是把sum(nums[j-k:j])和之前i-1组的最大值比较,找出最大值,然后所有的value中找到最小值,存放在dp[i][j]中。最后返回dp[m][n]就行了。
最后修改后大概5200ms的样子,dp

二分搜索

二分搜索大佬讲解的十分清晰,一看就懂,直接copy思路了。讲的很好:
  给定一个值x,判断是否存在一种划分使得每个子区间的和都不超过x,这是容易的。我们可以贪心地遍历一遍数组,不断求和,直到和超过了x值,再新分出一个子区间,最后检查划分出的子区间数是否超过了m。这个检查的时间复杂度为O(n). 然后就可以不断的询问x是否满足上述条件,如果满足说明我们要求的解不超过x,否则说明要求的解大于x,这就构成了一个二分的条件。我们先猜测x属于一个足够大的范围,然后检查中间值是否满足条件,不管结果如何,我们都可以将猜测区间减半。如此不断的缩减区间,就得到了最后的解。因为int的值不超过2^31,所以需要
O(log(2^31))=O(1)次检测,因此算法复杂度是O(n)的。
原文:https://blog.csdn.net/zlasd/article/details/53521265

有了这个思路,二分法的代码很容易写出来,(终于有自己会写的了):

class Solution:
    def splitArray(self, nums: List[int], m: int) -> int:
        l= max(nums)
        r = 2**32
        mid = 0
        def judge(nums,m,mid):
            s = nums[0]
            cnt=1
            for i in nums[1:]:
                if s+i>mid:
                    s=i
                    cnt+=1
                    if cnt>m:
                        return False
                else:
                    s+=i
            return True
        while l<r:
            mid = (l+r)//2
            if judge(nums,m,mid):
                r = mid
            else:
                l = mid+1
        return l

注意r取大一点,因为有个样例是INT_MAX。。。
用时是56ms,O(n)的算法就是快啊。
这个缩进的格式明天再改吧,今天先到这儿。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值