【題目】
給定一個整型數組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(
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