动态规划——游艇租赁问题

题目来自陈小玉教授的《趣学算法》,参考书中的讲解,结合自己的思考,便于以后复习。

问题

长江俱乐部在长江设置了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 n1站的最优解,是否具有最优子结构和重叠性。
在这里插入图片描述
因此可以使用动态规划的思想,该问题得最优解一定由子问题得最优解组成。
算法思路:
初始化 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)

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值