在一条直线上有n堆石子,每堆有一定的数量,每次可以将两堆相邻的石子合并,合并后放在两堆的中间位置,合并的费用为两堆石子的总数。求把所有的石子合并成一堆的最小花费。
采用动态规划思想,即区间dp,dp方程式如下:
dp[i][j] 表示第 i 到第 j 堆石子合并的最优解,sum[i][j] 表示第 i 到第 j 堆石子的总数量,所以 dp[i][j] 的最优解一定在 i 到 j 中间的两个最优解的和,然后加上本次的费用。
# @Time :2018/5/21
# @Author :Yinxing
def toneMerge(tone):
n = len(tone)
if n<=1: return 0
if n==2: return sum(tone) # 长度为2时,就是这两堆石头的和
dp = [[float('inf')]*n for _ in range(n)] # 初始化dp
for i in range(n): dp[i][i] = 0
sm = [tone[0]] # 记录 列表从0 到 n 的累加和
for i in range(1, n): sm.append(sm[i-1]+tone[i]) # 累加求和
for gap in range(1, n): # i 到 j 的间隔
for i in range(n-gap):
j = i + gap # 确定本次 i--j
tmp = sm[j] - [0, sm[i-1]][i > 0] # 获取第 i 到第 j 堆石子的总数量
for k in range(i, j): # 选择i 到 j 的最优解
dp[i][j] = min(dp[i][j], dp[i][k] + dp[k+1][j] + tmp)
return dp[0][n-1]
if __name__ == '__main__':
# tone = [7, 6, 5, 7, 100] # 175
# tone = [3, 4, 5, 6, 4, 2] # 61
# tone = [1, 2, 3, 4, 5] # 33
tone = [186, 64, 35, 32, 103] # 852
print(toneMerge(tone))
# @Time :2018/5/22
# @Author :Yinxing
def toneMerge(tone):
n = len(tone)
if n<=1: return 0
if n==2: return sum(tone) # 长度为2时,就是这两堆石头的和
dp = [[float('inf')]*(n+1) for _ in range(n+1)] # 初始化dp
sk = [[None]*(n+1) for _ in range(n+1)] # 初始化sk
for i in range(n+1): dp[i][i], sk[i][i] = 0, i
sm = [tone[0]] # 记录 列表从0 到 n 的累加和
for i in range(1, n): sm.append(sm[i-1]+tone[i]) # 累加求和
for gap in range(1, n): # i 到 j 的间隔
for i in range(n-gap):
j = i + gap # 确定本次 i--j
tmp = sm[j] - [0, sm[i-1]][i > 0] # 获取第 i 到第 j 堆石子的总数量
i1, j1 = sk[i][j-1], sk[i+1][j]+1 # 新的区间
for k in range(i1, j1): # 选择i 到 j 的最优解
if dp[i][j] > dp[i][k] + dp[k+1][j] + tmp:
dp[i][j], sk[i][j] = dp[i][k] + dp[k+1][j] + tmp, k # 更新dp, 并记录最有位置k
return dp[0][n-1]
if __name__ == '__main__':
# tone = [7, 6, 5, 7, 100] # 175
# tone = [3, 4, 5, 6, 4, 2] # 61
# tone = [1, 2, 3, 4, 5] # 33
tone = [186, 64, 35, 32, 103] # 852
print(toneMerge(tone))
当然还可以用GarsiaWachs算法实现,理论上时间复杂度可以达到nlogn。
发现问题,请记得留言指教哦