【题目】
给定一个整型数组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( N2∗M ) -> 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