其他題目---畫匠問題

【題目】

  給定一個整型數組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
發佈了183 篇原創文章 · 獲贊 418 · 訪問量 14萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章