动态规划之游艇租赁(python实现)

算法思想

动态规划也是一种分治思想,但与分治算法不同的是,分治算法是把原问题分解成若干子问题,自顶而下求解子问题,合并子问题的解,从而得到原问题的解。动态规划也是将原问题分解成若干子问题,然后自底向上,先求解最小的子问题,将结果存储在矩阵中,在求大的子问题时,直接从矩阵中查询小的子问题的解,避免重复计算。从而提高算法效率。
关键点:

  • 最优子结构,是指原问题的最优解包含其子问题的最优解
  • 子问题重叠,子问题重叠不是使用动态规划的必要条件,但是原问题存在子问题重叠更能体现动态规划的优势。

强烈推荐小白下载这个APP,快速搞懂算法运行流程。
算法动态图解:链接:https://pan.baidu.com/s/1mX3s7VjLTKLr7MZhAQO-6Q
提取码:cv5y

样例:游艇出租

长江沿线共有n个游艇出租站,游客可以从每个出租站租游艇,并且能够在下游的任何一个游艇出租站归还。游艇出租站 i i i到下一站 j j j的租金用矩阵update_rent[i][j] 表示。设计一个算法,求解从出租站 i i i到出租站 j j j的最少租金并打印出对应的路线。

分析

  • 1.分析最优解的结构特征

    • 假设我们已经知道在第 k k k个站点停靠会得到最优解,那么原问题就变成两个子问题:(i,i+1,…k),(k,k+1…j)之间的最少租金。
    • 那么原问题的最优解是否包含子问题的最优解呢?很显然是包含的。即最少租金 i i i-> j j j= i i i-> k k k+ k k k-> j j j
  • 2.建立最优值的递归式
    当j=i时,只有1个站点,update_rent[i][j] = 0;
    当j=i+1时,只有2个站点,update_rent[i][j] = 1;
    当j>i+1时,有3个以上的站点,update_rent[i][j] = min(update_rent[i][k]+update_rent[k][j],update_rent[i][j]),i<k<j;

python实现

# coding=utf-8
# 初始化最大堆
import numpy as np


# 1.初始化站点之间的租金,更新的租金和两地之间停靠点的index

init_rent = np.array([[0,2,6,9,15,20],[0,0,3,5,11,18],[0,0,0,3,6,12],[0,0,0,0,5,8],[0,0,0,0,0,6]])
update_rent = init_rent.copy()
stop_arr = np.full((6,6),-1) # -1代表两个站点之间是直达的,中间没有停靠。

# 2.按照递归公式进行计算
"""
当j=i时,只有1个站点,update_rent[i][j] = 0;
当j=i+1时,只有2个站点,update_rent[i][j] = 1;
当j>i+1时,有3个以上的站点,update_rent[i][j] = min(update_rent[i][k]+update_rent[k][j],update_rent[i][j]),i<k<j;
"""
def RentYacht(stops):
    """
    返回起始站到终点站的最短距离
    :param stops:
    :return:
    """
    #1.判断站点数量
    if stops == 1:
        return 0
    elif stops == 2:
        return init_rent[0][1] # 直接返回两个站点之间的距离
    else:
        for d in range(2,stops): # 将问题分解为n-2个子问题
            for i in range(stops-d):
                j = i+d
                for k in range(i+1,j): # 每个子问题的最优结果
                    temp = update_rent[i][k] + update_rent[k][j]
                    if temp<update_rent[i][j]:
                        update_rent[i][j] = temp
                        stop_arr[i][j] = k

def GetRoute(start = 0,end = 5):
    """
    获取游艇的路线和最短距离,以0站点到5站点为例
    :param stop_arr:
    :return:
    """
    if stop_arr[start][end] == -1: #表示直达
        print("--{}".format(end),end="")
        return
    GetRoute(start,stop_arr[start][end]) # 递归查找
    GetRoute(stop_arr[start][end],end)
    
if __name__ == "__main__":
    RentYacht(6)
    print("初始化租金矩阵:\n",init_rent)
    print("更新后的租金矩阵:\n",update_rent)
    print("更新后的站点矩阵:\n",stop_arr)
    print("从0站点到5站点,话费的最少租金为:{}".format(update_rent[0][-1]))
    print("最少租金经过的站点:0",end="")
    GetRoute()
                   

复杂度分析

时间复杂度主要取决嵌套3层for循环,最坏情况为语句执行 O ( n 3 ) O(n^3) O(n3)

猜你喜欢

转载自blog.csdn.net/weixin_42662358/article/details/100151752