游艇租赁最小代价——动态规划求解

问题:江上有6个游艇站,游客可以从任意一个站租赁游艇,并在其下游任意一个站归还游艇,不同站之间的费用不同。

游艇出租站i到j之间的租金为r(i,j)。上下游情况以及各站点之间的费用如下:

图片来源于陈小玉老师的《趣学算法》

分析:

1.上述指出上游可以到下游中任意一个站规划游艇,那么,智能是序号低的站到序号高的站,所以,在租金矩阵中只会存在右三角的部分。

2.上述问题无外乎考虑的是直达还是经过中转然后到达目的站点。

3.所以,在出发站点与目的站点之间可以考虑经过0次,1次,2次......中转,最后到达目的站点。

4.要是经过1次或者一次以上中转,那么我们需要记录初始站点i到目的站点j的中转站点k到底是谁,所以我们需要额外数组进行记录,我们选用s[i][j]的值表示两点之间的中转节点。

5.而且我们还需要不断更新初始站点i到目的站点j的最短花费情况,所以要用新的数组进行记录,我们选用m[i][j]的值表示两点之间的最短花费。

注:我们需要进行以下推论,若存在1,2,3站点,1->3有两种情况,要么1->3,要么1->2->3,要是经过2站点中转,则1->3的花费最小,那么我们就知道了1->3的最佳策略是在2站点进行中转。

那么,继续,存在1,2,3,4站点,1->4的情况就更多了些,可以是1->4,也可以是1->2->4,或者是1->3->4,又或者是1->2->3->4这4中情况,有上述中的假设,1->3的最佳策略是在2站点进行中转,那么用于1->4策略中,1->2->3->4的情况一定优于1->3->4的情况。

6.所以,这就引出了动态规划的核心要点,即原问题的最优解一定包含了子问题的最优解,即1->4(1->3->4)的最优解情况一定包含了1->3的最优解情况,那么,我们在开始要记录了子问题的最优解情况,后续则可以直接使用,

7.在6的基础上,我们开始建立最优值的递归式:

已知我们的数据结构:m[i][j]的值表示两点之间的最短花费,s[i][j]的值表示两点之间的中转节点。

那么,若i==j,则m[i][j]==0;

若j==i+1,则m[i][j]==r[i][j],即m[i][j]表示两点间的直达代价,那么s[i][j]==0,因为s[i][j]表示两者间的中转点,而两点为直达,所以没有中转点;

若j>i+1,即两点间存在一个或者一个以上的中转点时,m[i][j]==min{m[i][k]+m[k][j],r[i][j]}

注:本人曾经在此处有过疑惑。但其实想一想就可以理解了,假设i到j有最优情况,那么假设其中一个中转节点是k,那么,我们可以知道最直接的路径i->k->j是一条最优路径,可能i->k之间,k->j之间都还存在这中转情况。

但是我们知道i->k的代价,k->j的代价都是最小的(子问题最优用于母问题最优)

而事实上,i->k的最优情况在分析i->j之前我们已经得到,我们直接用就可以,没必要再重新分析。就是比如先以三个点为例,结果1->3的代价中,路径1->2->3的代价是低于路径1->3代价的,那么,我们就会在选择中记录要是有结果1->3的情况,我们就默认选择路径1->2->3。

同理在1->4的结果中,我们会尝试路径1->4,1->2->4,1->2->3->4而不会去尝试1->3->4。而在计算经过节点3进行中转的情况时,我们只需要关注的是3->4之间的代价,而结果1->3的代价已经由路径1->2->3求得。

所以,最后,若假设结果1->4的最佳路径是1->2->3->4,我们只需要知道4的前一节点是3,而不关心3的前一节点是谁,而到了3节点,我们才能发现其前一节点还需要经过2。

接下来进行代码分析:

初始化,我们让m[i][j]=r[i][j],s[i][j]=0,即将直达情况进行了第一遍记录,后续的比较就是逐次增加中转站点的个数,不断进行比较是经过中转后的代价最优还是直达最优,从而对代价表与中转表进行更新。

接下来,分析超过2个节点的情况,我们加入某个点作为两点间的中转点,看加入后代价是否优于之前,若代价优于直达,更新代价表,并将中转节点编号赋值给r[i][j]。

接下来上代码:

 1 #include <iostream>
 2 using namespace std;
 3 const int N = 1024;
 4 int r[N][N],m[N][N],s[N][N];
 5 int n;
 6
 7 void initArray()    //初始化数组
 8 {
 9     for(int i = 1; i <= n; i++){
10         for(int j = i+1; j <= n; j++){
11             m[i][j] = r[i][j];
12             r[i][j] = 0;
13         }
14     }
15 }
16
17 void calShortValue()    //计算数组中的最优代价情况
18 {
19     for(int i = 1; i <= n; i++){
20         for(int j = i+2; j <= n; j++){
21             for(int k = i+1; k < j; k++){
22                 if(m[i][k] + m[k][j] < m[i][j]){
23                     m[i][j] = m[i][k] + m[k][j];
24                     s[i][j] = k;
25                 }
26             }
27         }
28     }
29 }
30
31 void printShortValue()  //打印数组中最优代价情况
32 {
33     for(int i = 1; i <= n; i++){
34         for(int j = 1; j <= n; j++){
35             if(j <= i){
36                 cout << " ";
37             }
38             else{
39                 cout << m[i][j];
40             }
41             cout << " ";
42         }
43         cout << "\n";
44     }
45 }
46
47 int main()
48 {
49     cout << "请输入站点个数:" << endl;
50     cin >> n;
51     cout << "请以此输入各站点间的代价:" << endl;
52     for(int i = 1; i <= n; i++){
53         for(int j = i+1; j <= n; j++){
54             cin >> r[i][j];
55         }
56     }
57     initArray();
58     calShortValue();
59     printShortValue();
60 }

原文地址:https://www.cnblogs.com/chenleideblog/p/10470326.html

时间: 2024-10-14 12:38:57

游艇租赁最小代价——动态规划求解的相关文章

[Swust OJ 404]--最小代价树(动态规划)

题目链接:http://acm.swust.edu.cn/problem/code/745255/ Time limit(ms): 1000 Memory limit(kb): 65535 Description 以下方法称为最小代价的字母树:给定一正整数序列,例如:4,1,2,3,在不改变数的位置的条件下把它们相加,并且用括号来标记每一次加法所得到的和. 例如:((4+1)+ (2+3))=((5)+(5))=10.除去原数不4,1,2,3之外,其余都为中间结果,如5,5,10,将中间结果相加

关于最小代价子母树

第一次尝试写动态规划(Dynamic Planning)= 问题如下: ------------------------------------------------------------------------------------------------------------------------- 最小代价子母树 设有一排数,共n个,例如:22 14 7 13 26 15 11.任意2个相邻的数可以进行归并,归并的代价为该两个数的和,经过不断的归并,最后归为一堆,而全部归并代价的

【算法导论之七】动态规划求解最长公共子序列

一.动态规划的概念 动态规划(Dynamic Programming)是通过组合子问题的解而解决整个问题的.分治算法是指将问题划分成一些独立的子问题,递归地求解各子问题,然后合并子问题的解而得到原始问题的解,与此不同,动态规划适用于子问题不是独立的情况,也就是各个子问题包含公共的子问题.在这种情况下,采用分治法会做许多不必要的工作,即重复地求解公共地子问题.动态规划算法对每个子问题只求解一次,将其结果保存在一张表中,从而避免每次遇到各个子问题时重新计算答案. 动态规划通常应用于最优化问题.此类问

图论-最短路-dijkstra / 0-1BFS-使网格图至少有一条有效路径的最小代价

2020-03-01 22:59:59 问题描述: 给你一个 m x n 的网格图 grid . grid 中每个格子都有一个数字,对应着从该格子出发下一步走的方向. grid[i][j] 中的数字可能为以下几种情况: 1 ,下一步往右走,也就是你会从 grid[i][j] 走到 grid[i][j + 1] 2 ,下一步往左走,也就是你会从 grid[i][j] 走到 grid[i][j - 1] 3 ,下一步往下走,也就是你会从 grid[i][j] 走到 grid[i + 1][j] 4 

动态规划求解最长公共子序列

动态规划算法与分治法类似,其基本思想也是将待求解问题分解成若干个子问题,先求解子问题,然后从这些子问题的解得到原问题的解.与分治法不同的是,适合于用动态规划求解的问题,经分解得到的子问题往往不是互相独立的.若用分治法来解决这类问题,则分解得到的子问题数目太多,以至于最后解决原问题需要耗费指数时间.然而,不同子问题的数目常常只有多项式量级.在用分治法求解时,有些子问题被重复计算了许多次.如果我们能够保存已解决的子问题的答案,而在需要时再找出已求得的答案,这样就可以避免大量的重复计算,从而得到多项式

动态规划求解最大字段和及其变种问题

动态规划(Dynamic Programming, DP)为一常用算法思想,本文讲述如何利用DP解决常见的最大字段和及其变种问题. 一. 最大字段和问题 问题定义 设数组为a[k],1≤k≤n,最大字段和X定义为: X=max1≤i≤j≤n{∑k=ija[k]} X直观含义即,求任一连续字数组的最大和. 问题分析 不妨设: b[j]=max1≤m≤j{∑k=mja[k]} 其中,1≤j≤n b[j]的直观含义为,以a[j]为结束元素的连续数组的最大和. 由X和b[j]的定义,易知: X=max1

动态规划求解0-1背包问题

0-1背包问题是: 一个背包能承受的最大容量为max_weight,  现在有n个物品, 它们的重量分别是{w1,w2,w3,......wn}, 和价值分别是{v1,v2,......vn}, 现在要求在满足背包装载的物品不超过最大容量的前提下,保证装载的物品的价值最大? 动态规划求解过程可以这样理解: 对于前i件物品,背包剩余容量为j时,所取得的最大价值(此时称为状态3)只依赖于两个状态. 状态1:前i-1件物品,背包剩余容量为j.在该状态下,只要不选第i个物品,就可以转换到状态3. 状态2

1008-----算法笔记----------0-1背包问题(动态规划求解)

1.问题描述 给定n种物品和一个背包,物品i的重量是wi,其价值为vi,背包的容量为C.问:应该如何选择装入背包的物品,使得装入背包中物品的总价值最大? 2.问题分析 上述问题可以抽象为一个整数规划问题,即求满足 (a)Σwixi ≤ C:(b)xi ∈(0,1),  1≤i≤n:条件下,∑vixi最大时的一个物品xi序列.分析问题可以发现,该问题具有最优子结构性质,那么就可以尝试用动态规划方法求解,而动态规划求解的关键就是列出问题的递归关系表达式. 设m(i,j)为背包容量为j,可选物品为i,

[swustoj 404] 最小代价树

最小代价树(0404) 问题描述 以下方法称为最小代价的字母树:给定一正整数序列,例如:4,1,2,3,在不改变数的位置的条件下把它们相加,并且用括号来标记每一次加法所得到的和. 例如:((4+1)+ (2+3))=((5)+(5))=10.除去原数不4,1,2,3之外,其余都为中间结果,如5,5,10,将中间结果相加,得到:5+5+10= 20,那么数20称为此数列的一个代价,若得到另一种算法:(4+((1+2)+3))=(4+((3)+3))=(4+(6))=10,数列的另一个代价为:3+6