题目来自陈小玉教授的《趣学算法》,参考书中的讲解,结合自己的思考,便于以后复习。
问题
长江俱乐部在长江设置了6个游艇出租站1,2,…6,游客可在这些游艇出租站租用游艇,并在下游的任何一个游艇出租站归还游艇。游艇出租站 i i i到游艇出租站 j j j之间的租金为 r ( i , j ) r(i,j) r(i,j), 现在要求游艇从出租站1到出租站6所需要的最少租金。
r(i,j):
1 2 3 4 5 6
1 [ 0 2 6 9 15 20]
2 [ 0 0 3 5 11 18]
3 [ 0 0 0 3 6 12]
4 [ 0 0 0 0 5 8]
5 [ 0 0 0 0 0 6]
6 [ 0 0 0 0 0 0]
思路
暴力搜索
如果使用暴力搜索方法的话,可以依次遍历只停靠1次 C 4 1 C_4^1 C41,停靠2次 C 4 2 C_4^2 C42,停靠3次 C 4 3 C_4^3 C43,停靠4次 C 4 4 C_4^4 C44,一共 C 4 1 + C 4 2 + C 4 3 + C 4 4 C_4^1+C_4^2+C_4^3+C_4^4 C41+C42+C43+C44种可能,如果中间可停靠码头数目特别多的话( n n n个),搜索空间会特别大, C n 1 + . . . + C n n C_n^1+...+C_n^n Cn1+...+Cnn。
动态规划
所以考虑是不是可以利用动态规划的思想,将一个大问题化成很多小问题。但是使用动态规划需要一个前提:从第1站到第
n
n
n站的最优解包含前
n
−
1
n-1
n−1站的最优解,是否具有最优子结构和重叠性。
因此可以使用动态规划的思想,该问题得最优解一定由子问题得最优解组成。
算法思路:
初始化
m
[
i
]
[
j
]
=
r
[
i
]
[
j
]
m[i][j]=r[i][j]
m[i][j]=r[i][j]储存各子问题最低代价,初始化
s
[
i
]
[
j
]
=
0
s[i][j]=0
s[i][j]=0存储各个子问题最低代价的停靠点。
interval表示跨度为interval的两个站点。逐渐增大interval,依次算它们的最小花费,直到最后的终点。
因为相邻两点的代价已经知道,interval=1不需要考虑。
- interval=2
中间停靠点只有一个,依次遍历求得1到3,2到4,3到5,4到6的最低代价。 - interval=3
依次遍历求得1到4,2到5,3到6的最低代价。中间停靠点有2个,需要依次考虑。例如:1到4,最优的停靠点可能是2或3。 - interval=4
依次遍历求得1到5,2到6。中间停靠点有3个,需要依次考虑。因为间隔为2两点的最优解已经算过,直接查表即可。 - interval=5
求得1到6的代价,中间停靠点可能有4个,依次遍历,最优值即为最优解。
根据 s [ i ] [ j ] s[i][j] s[i][j]可得到需要停靠哪些点。
python代码实现:
import numpy as np
# 1号点到2、3。。。6节点的代价
# 2号点到3。。。6节点的代价
# 。。。
# 5号点到6号节点的代价
r = [
[2, 6, 9, 15, 20],
[3, 5, 11, 18],
[3, 6, 12],
[5, 8],
[6]
]
def leastCost(r):
# 一共points个点
points = len(r) + 1
# 初始化记录代价和停靠点矩阵
m = np.zeros((points, points), dtype=np.int16)
s = np.zeros((points, points), dtype=np.int16)
# 为m矩阵赋初值等于r
for row in range(len(r)):
for col in range(len(r[row])):
m[row][row+col+1] = r[row][col]
print(m)
# 遍历不同间隔
for interval in range(2, points):
# 遍历不同起点
for start in range(points - interval):
end = start + interval
# 遍历两点间不同中间节点
for k in range(start, end):
if m[start][k] + m[k][end] < m[start][end]:
m[start][end] = m[start][k] + m[k][end]
s[start][end] = k
print(m)
print(s)
# 显示途径停靠节点的结果
showStopPoint(s, 0, len(r))
def showStopPoint(s, start, destination):
stopPoint = s[start][destination]
if stopPoint != 0:
# 递归查看停靠的节点
showStopPoint(s, start, stopPoint)
print('停靠点:', stopPoint+1)
showStopPoint(s, stopPoint, destination)
leastCost(r)