参考资料:
石子合并问题–动态规划;贪心
石子合并问题是最经典的DP问题。首先它有如下3种题型:
(1)有N堆石子,现要将石子有序的合并成一堆,规定如下:每次只能移动任意的2堆石子合并,合并花费为新合成的一堆石子的数量。求将这N堆石子合并成
分析:当然这种情况是最简单的情况,合并的是任意两堆,直接贪心即可,每次选择最小的两堆合并。本问题实际上就是哈夫曼的变形。
(2)有N堆石子,现要将石子有序的合并成一堆,规定如下:每次只能移动相邻的2堆石子合并,合并花费为新合成的一堆石子的数量。求将这N堆石子合并成一堆的总花费最小(或最大)。
(3)问题(2)的是在石子排列是直线情况下的解法,如果把石子改为环形排列,又怎么做呢?
---------------------
作者:JeanCheng
来源:CSDN
原文:https://blog.csdn.net/gatieme/article/details/49206193
版权声明:本文为博主原创文章,转载请附上博文链接!
为什么第一问贪心算法会得到最优的解法?
证明:石子堆看成是树的叶子,每次我们合并两个堆得到一个新的堆,然后继续和其他堆继续合并,得到一棵树。总的代价为每个叶子节点的代价乘以其到根节点的路径长度。贪心算法得到的树,叶子节点代价小的总是更长或者相等,其他算法至少有两个叶子节点(大小不同)跟贪心算法相反,根据树的结构,代价更大的叶子节点的路径更长,这样得到的树的代价肯定是更大的。第二个问题:动态规划
下面程序中p矩阵存放的是从哪里分成两半是最优的
import numpy as np
def merge_stones(stones):
num_stones = len(stones)
m = np.zeros(shape=(num_stones,num_stones))
p = np.zeros(shape=(num_stones,num_stones))
for i in range(num_stones - 1):
m[i][i+1] = stones[i] + stones[i+1]
for i in range(1,num_stones-1):
for j in range(num_stones - 1 - i):
k = j + i + 1
min = 10000000000000
for l in range(j+1,k):
temp = m[j][l] + m[l+1][k] + np.sum(stones[j:k+1])
if min > temp:
min = temp
min_p = l
m[j][k] = min
p[j][k] = min_p
return m[0][num_stones-1],p
def print_ret(stones,p,start,last,first_part=False):
if start < last:
print('(',end='')
mid = int(p[start][last])
print_ret(stones,p,start,mid,True)
print_ret(stones,p,mid+1,last,False)
print(')',end='')
else:
print(stones[start],end='')
if first_part:
print(',',end='')
if __name__ == '__main__':
# while True:
# num_stones = int(input('Input:\n'))
# stones = []
# for s in map(int, input().split()):
# stones.append(s)
#
# if num_stones == len(stones):
# merge_stones(stones)
# else:
# print('输入数量不对')
max,p = merge_stones([4,4,5,9])
print_ret([4,4,5,9],p,0,len([4,4,5,9])-1)
- 第三个问题:跟上面的问题相似,只是有一个圈罢了。