问题描述:

长江游乐俱乐部在长江上设置了n个游艇出租站,游客可以在这些游艇出租站用游艇,并在下游任何一个游艇出租站归还游艇,游艇出租站i到j之间的租金是rent(i,j),其中1<=i<j<=n。试设计一个算法使得游客租用的费用最低。

这是一道典型的动态规划问题。解题的思路是,既然要得到最小的花费,那么就从最底层开始,逐层向上计算每两个站之间的最小花费,并记录在数组中,有记录的就不必再算了。其中两个站可能是相邻的,也可能不是相邻的。然后要得到方案,就需要对费用进行递归检测,比如:如果J、K两站之间的最小费用为M,但是J、K两站之间的某一站P满足:JP最小费用+PK最小费用等于JK最小费用,同时JK最小费用不等于由J直接到K的费用,则这个P站肯定是其中一个租船点。如果JK最小费用等于由J直接到K的费用,那么J和K必定是两个租船点,且其中没有租船点。在递归的过程中将这些租船点记录下来,就得到了最优方案。

代码如下:

#include <iostream>
#include <memory.h>
#include <stdio.h>
#define N 200
using namespace std;
int p[N][N];    //p[i][j]为i到j的最小费用
int r[N][N];    //存储数据
int mark[N][N]; //记录是否已经计算过最小费用,避免重复计算
int smallestFee(int start, int ende);    //求最小费用
void findPath(int start, int ende);      //求最小费用方案
int answer[N];int main()
{int n;while(1 == scanf("%d", &n)) //读取数据{memset(answer, 0, sizeof(answer));memset(mark, 0, sizeof(mark));for(int i = 1; i <= n - 1; ++i){for(int j = i + 1; j <= n; ++j)scanf("%d", &r[i][j]);}smallestFee(1, n);printf("Smallest cost: %d\n", p[1][n]);  //输出最小费用//找出最优解findPath(1, n);printf("Path: 1->");for(int i = 1; i < n; ++i)if(answer[i] != 0) printf("%d->", answer[i]);printf("%d\n\n", n);while(getchar() != '\n')    //剔除空格,为下一次测试做准备continue;}return 0;
}int smallestFee(int start, int ende)
{if(start + 1 == ende)            //分解到只剩下2个站{p[start][ende] = r[start][ende];mark[start][ende] = 1;return r[start][ende];}p[start][ende] = r[start][ende];    //假设直接从start到ende为最小花费int k, x1, x2;for(k = start + 1; k < ende; ++k)       //找从start到ende的最小花费{//计算过最小费用则不用再次计算if(mark[start][k] != 1) x1 = smallestFee(start, k);else x1 = p[start][k];if(mark[k][ende] != 1) x2 = smallestFee(k, ende);else x2 = p[k][ende];if(p[start][ende] > x1 + x2)p[start][ende] = x1 + x2;}mark[start][ende] = 1;   //已经计算过标记为1return p[start][ende];
}void findPath(int start, int ende)
{if((start + 1 == ende) || (p[start][ende] == r[start][ende]))    //寻找到相邻位置,或者已经是最便宜,记录位置{answer[ende] = ende;return;}for(int k = start + 1; k < ende; ++k){if(p[start][k] + p[k][ende] == p[start][ende]){findPath(start, k);findPath(k, ende);return;     //找到了直接返回}}return;
}

然而上面这种简单的带备忘的朴素分治算法,效率并不高,最差劲的是在求最小花费的过程中,不能同时构建最优方案。下面给出一种更好的方法。

这种方法假定第一次还的位置为k,从1直接到k是最优的(k从2到n循环)。然后递归调用此方法,求得k到n的最优方案和花费。这种方法是一种自顶向下的计算方法,也用到了备忘录。待会儿再给出一种非递归的自底向上的方法。

#include <iostream>
#include <memory.h>
#include <stdio.h>
#define N 200
using namespace std;
int r[N][N];    //存储数据
int p[N][N];    //记录最小花费
int smallestFee(int start, int n);    //求最小费用
int answer[N];int main()
{int n;while(1 == scanf("%d", &n)) //读取数据{memset(answer, 0, sizeof(answer));memset(p, 0, sizeof(p));for(int i = 1; i <= n - 1; ++i){for(int j = i + 1; j <= n; ++j)scanf("%d", &r[i][j]);}printf("Smallest cost: %d\n", smallestFee(1, n));  //输出最小费用//输出方案printf("Path: 1->");for(int i = 2; i < n; ++i)if(answer[i] != 0) printf("%d->", answer[i]);printf("%d\n\n", n);while(getchar() != '\n')    //剔除后续字符,为下一次输入做准备continue;}return 0;
}/*自顶向下递归计算*/
int smallestFee(int start, int n)
{if(start == n){p[start][n] = r[start][n];return 0;}int smallest = 1 << 10;int x;for(int k = start + 1; k <= n; ++k){int temp = r[start][k];if(p[k][n] != 0)    //如果计算过就不必再计算temp += p[k][n];elsetemp += smallestFee(k, n);if(temp < smallest){smallest = temp;x = k;}}answer[x] = x;              //记录最优的归还站点位置p[start][n] = smallest;     //记录最优花费return smallest;
}

上面两种递归方法,因为带备忘录,所以没有重复计算,计算最小花费的时间复杂度均为O(n²),但第二种更好一些,因为在计算的过程中就能构建最优方案,而且第一种还用另一个函数计递归求解了最优方案。

下面给出一种非递归的方法,很容易知道时间复杂度也为O(n²),但是不需要备忘录,其在求解最小费用时,同时能够构造最优解。这是一种自底向上的方法。假若把n个站从左到右排成一排,左边为1(起点),右边为n(终点)。先计算1到k的最优值,在根据1到k的最优值,计算1到k+1的最优值,最后得到1到n的最优值。

代码如下:

#include <iostream>
#include <memory.h>
#include <stdio.h>
#define N 200
using namespace std;
int r[N][N];    //存储费用数据
int p[N][N];    //记录最小花费
int smallestFee(int start, int n);    //求最小费用
int answer[N];int main()
{int n;while(1 == scanf("%d", &n)) //读取数据{memset(answer, 0, sizeof(answer));memset(p, 0, sizeof(p));for(int i = 1; i <= n - 1; ++i){for(int j = i + 1; j <= n; ++j)scanf("%d", &r[i][j]);}printf("Smallest cost: %d\n", smallestFee(1, n));  //输出最小费用//输出方案printf("Path: 1->");for(int i = 2; i < n; ++i)if(answer[i] != 0) printf("%d->", answer[i]);printf("%d\n\n", n);while(getchar() != '\n')    //剔除后续字符,为下一次输入做准备continue;}return 0;
}/*自底向上计算*/
int smallestFee(int start, int n)
{//这两层循环,由左至右,计算了在下次停靠归还游艇之前的最小费用for(int i = 2; i <= n; ++i){int x = 2 << 10;int temp;for(int j = 1; j < i; ++j){//如果从1到j站的最小费用加上从j直接到i站的费用比之前的最优方案更优,则选择这种方案if(r[j][i] + p[1][j] < x){x = r[j][i] + p[1][j];temp = j;}}p[1][i] = x;            //记录最少花费answer[temp] = temp;    //记录最优方案的归还站点}return p[start][n];
}

后面两种方法的思路是在看《算法导论》动态规划章节的过程中得来的,如果文中有错误请批评指正。

动态规划:游艇租用问题相关推荐

  1. 【动态规划】最大K乘积问题和游艇租用问题——武汉理工大学算法设计与分析课程实验

    1.  最大K乘积问题 « 问题描述 设I是一个n位十进制整数.如果将I划分为k段,则可得到k个整数.这k个整数的乘积称为I的一个k乘积.试设计一个算法,对于给定的I和k,求出I的最大k乘积. 例如十 ...

  2. 动态规划法求解游艇租用问题C++代码

    动态规划法求解游艇租用问题 游艇租用问题:长江旅游俱乐部在长江上设置了N个游艇出租站0,1,2,-,N-1,游客在这些站中租用游艇,并在下游的任何一个游艇出租站归还,游艇出租站i到游艇出租站j之间的租 ...

  3. 【动态规划】游艇租用问题(c++)

    1.首先放题目  « 问题描述 长江游艇俱乐部在长江上设置了n个游艇出租站1,2,-,n.游客可在这些游艇出租站租用游艇,并在下游的任何一个游艇出租站归还游艇.游艇出租站i到游艇出租站j之间的租金为r ...

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

    题目来自陈小玉教授的<趣学算法>,参考书中的讲解,结合自己的思考,便于以后复习. 问题 长江俱乐部在长江设置了6个游艇出租站1,2,-6,游客可在这些游艇出租站租用游艇,并在下游的任何一个 ...

  5. 游艇租用问题算法c语言,租用游艇问题  算法设计分析

    长江游艇俱乐部在长江上设置了n 个游艇出租站1,2,-,n.游客可在这些游艇出租站租用游艇,并在下游的任何一个游艇出租站归还游艇.游艇出租站i 到游艇出租站j 之间的租金为r(i,j),1≤i< ...

  6. 视网膜屏幕_不要忘记视网膜屏幕上的图标

    视网膜屏幕 Thomas Fuchs needs no introduction. I've looked up to Thomas' animation artistry since his Pro ...

  7. 7-11 租用游艇问题 (15 分)(思路+详解+一步步分析+网格解决动态规划问题)Come boy!!!!

    一:题目 题目来源:王晓东,<算法设计与分析> 长江游艇俱乐部在长江上设置了n个游艇出租站1,2,-,n.游客可在这些游艇出租站租用游艇,并在下游的任何一个游艇出租站归还游艇.游艇出租站i ...

  8. 算法租用游艇问题c语言,动态规划租用游艇问题

    租用游艇动态规划解决 长江俱乐部在长江设置了n个游艇出租站1,2,-n,游客可在这些游艇出租站租用游艇,并在下游的任何一个游艇出租站归还游艇.游艇出租站i到游艇出租站j之间的租金为r(i,j),设计一 ...

  9. 租用游艇问题 石子合并问题 动态规划实验

    实验名称:动态规划 一.实验预习 1.实验目的 1. 理解并掌握动态规划方法的设计思想: 2. 提高应用动态规划方法解决问题和设计算法的能力: 3. 通过编程实现租用游艇问题和石子合并问题,进一步理解 ...

最新文章

  1. WPF中获取鼠标相对于桌面位置
  2. retinaface 记录
  3. docker 部署redis
  4. Runtime 总结
  5. XCode中安装cocoapods步骤
  6. 基于组件的案例:购物车
  7. allocator类编程实验
  8. 全国计算机等级考试题库二级C操作题100套(第67套)
  9. 【数论】挖掘机技术哪家强(jzoj 3858)
  10. Honeycomb——BFS
  11. [Leetcode][第32题][JAVA][最长有效括号][动态规划][栈][正向逆向结合]
  12. 你知道group by的工作原理和优化思路吗?
  13. SXSSFWorkbook使用——使用excel模板
  14. spring读取properties配置文件_Spring-1
  15. Windows端高仿超级逼真Mac系统方法
  16. linux 软路由_软路由和硬路由的区别分析
  17. 一起Talk Android吧(第三百二十六回:Android中的布局编辑器)
  18. 矩阵分解实现个性化推荐算法实践
  19. Android中的占位符
  20. 荣耀简史:起于抗击小米、止于拯救华为

热门文章

  1. 华工计算机答辩,华工“民间论文答辩”怎么有趣怎么来
  2. 【差异分析】蓝牙4.0 vs 蓝牙4.1 vs 蓝牙4.2 vs 蓝牙5.0
  3. 广东省计算机等级考试一级试题(1),全国计算机等级考试一级B模拟试题及答案(1)...
  4. deep learning中网络的层数和每层的节点数是如何确定的?
  5. 2021年PHP-Laravel面试题问卷题 答案记录
  6. PaddleHub实现交通标志识别
  7. 微信小程序云开发实战:网上商城(一)
  8. data guard日常维护及管理
  9. Linux 入门 Linux发展史及常用命令
  10. OTB100/2015 matlab toolkit的使用