其他题目---画匠问题

给定一个正数数组arr表示画作时间,和画匠数量num,要找到完成所有画作的最短时间。当画匠数量大于画作数量时,返回最大值;否则,通过动态规划求解,考虑不同画匠分配策略,找到所需时间的最小值。通过空间压缩和四边形不等式优化,降低时间复杂度。
摘要由CSDN通过智能技术生成

【题目】

  给定一个整型数组arr,数组中的每个值都为正数,表示完成一幅画作需要的时间,再给定一个正数num表示画匠的数量,每个画匠只能画连在一起的画作。所有的画匠并行工作,请返回完成所有的画作需要的最少时间。

【基本思路】

  方法一。如果画匠的数量大于画作的数量,每个人完成一幅画就是最优解,即返回arr中的最大值。如果只有一个画匠,那么对于这个画匠来说,arr[0…j]上的画作最少时间就是arr[0…j]的累加和。如果有两个画匠,有如下几种方案:

  1、第一个画匠负责arr[0],第二个画匠负责arr[1…j],所需的时间为max{sum[0], sum[1…j]}。
  2、第一个画匠负责arr[0…1],第二个画匠负责arr[2…j],所需的时间为max{sum[0…1], sum[2…j]}。
  3、第一个画匠负责arr[0…k],第二个画匠负责arr[k+1…j],所需要的时间为max{sum[0…k], sum[k+1…j]}。

  所有情况中所需要时间最少的就是最终的答案。

  当画匠数量大于2时,假设dp[i][j]表示i个画匠搞定arr[0…j]这些画所需要的最少时间,那么有如下几种方案:

  1、第1~i-1个画匠负责arr[0],第i个画匠负责arr[1…j],所需的时间为max{dp[i-1][0], sum[1…j]}。
  2、第1~i-1个画匠负责arr[0…1],第i个画匠负责arr[2…j],所需的时间为max{dp[i-1][1], sum[2…j]}。
  3、第1~i-1个画匠负责arr[0…k],第i个画匠负责arr[k+1…j],所需要的时间为max{dp[i-1][k], sum[k+1…j]}。

  哪种情况所需要的时间最少,dp[i][j]的值就等于哪个。下面是使用空间压缩后的动态规划的代码:

#python3.5
def painterQuestion1(arr, num):
    if arr == None or len(arr) == 0 or num < 1:
        raise Exception("Error!")
    if len(arr) == 1:
        return arr[0]
    if len(arr) <= num:
        minTime = arr[0]
        for i in range(len(1, arr)):
            minTime = max(minTime, arr[i])
        return minTime
    sumArr = [0 for i in range(len(arr))]
    map = [0 for i in range(len(arr))]
    sumArr[0] = arr[0]
    map[0] = arr[0]
    for i in range(1, len(arr)):
        sumArr[i] = sumArr[i-1] + arr[i]
        map[i] = sumArr[i]
    for i in range(1, num):
        for j in range(len(arr)-1, i-1, -1):
            minTime = sys.maxsize
            for k in range(i-1, j+1):
                minTime = min(minTime, max(map[k], sumArr[j]-sumArr[k]))
            map[j] = minTime
    return map[-1]

  方法二。使用 “四边形不等式”优化动态规划。

  假设计算dp[i-1][j]时,在最好的划分方案中,第i-1个画匠负责arr[a…j]的画作。在计算dp[i][j+1]时,在最好的划分方案中,第i个画匠负责arr[b…j]的画作。那么在计算dp[i][j]时,假设最好的划分方式是让第i个画匠负责arr[k…j],那么k的范围一定是[a, b],这样就省去了很多无效的枚举过程,可以将时间复杂度降低一个维度,O( N2M ) -> O( N2 )。具体实现参见如下代码:

def painterQuestion2(arr, num):
    if arr == None or len(arr) == 0 or num < 1:
        raise Exception("Error!")
    if len(arr) == 1:
        return arr[0]
    if len(arr) <= num:
        minTime = arr[0]
        for i in range(1, len(arr)):
            minTime = max(minTime, arr[i])
        return minTime
    sumArr = [0 for i in range(len(arr))]
    map = [0 for i in range(len(arr))]
    sumArr[0] = arr[0]
    map[0] = arr[0]
    for i in range(1, len(arr)):
        sumArr[i] = sumArr[i-1] + arr[i]
        map[i] = sumArr[i]
    cands = [0 for i in range(len(arr))]
    for i in range(1, num):
        for j in range(len(arr)-1, i-1, -1):
            minEnum = cands[j]
            maxEnum = j if j == len(arr)-1 else cands[j+1]
            minTime = sys.maxsize
            for k in range(minEnum, maxEnum+1):
                cur = max(map[k], sumArr[j] - sumArr[k])
                if cur < minTime:
                    minTime = cur
                    cands[j] = k
            map[j] = minTime
    return map[-1]


def painterQuestion3(arr, num):
    def getNeedNum(arr, limit):
        res = 1
        sum = 0
        for i in range(len(arr)):
            if arr[i] > limit:
                return sys.maxsize
            sum += arr[i]
            if sum > limit:
                res += 1
                sum = arr[i]
        return res

    if arr == None or len(arr) == 0 or num < 1:
        raise Exception("Error!")
    if len(arr) == 1:
        return arr[0]
    if len(arr) <= num:
        minTime = arr[0]
        for i in range(1, len(arr)):
            minTime = max(minTime, arr[i])
        return minTime
    minSum = 0
    maxSum = 0
    for i in range(len(arr)):
        maxSum += arr[i]
    while minSum != maxSum - 1:
        mid = (minSum + maxSum) // 2
        if getNeedNum(arr, mid) > num:
            minSum = mid
        else:
            maxSum = mid
    return maxSum
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值