过河问题

问题描述:
一群人过河,每个人体重不同,过河需要的时间不同,体重多少,过河时间就要多少, T[1], T[2], … , T[n],已经按从小到大排好序。只有一只船,船每次最多只能载2个人,这两个人过河的最短时间取决于体重最重的那个人,2个人过河之后需要一个人将船划回来供后面的人用。问:所有人过河,需要的最短时间时多少?
输入:人数n,接下来一行是n个人的体重
示例

输入
4
2 10 12 11
输出
39

输入
4
2 7 3 8
输出
19

这个题乍看是让过河时间最短的那个人来回的和其他人过河,因为当他和别人过河之后再将船划回来所需的时间最短。
这个推论看起来是对的,但是其实是有问题的:
比如,有4个人过河,体重(即过河时间)分别为2,3, 7, 8

可以看到这种貌似正确的方法所花的时间为:
3 + 2 + 8 + 2 + 7 = 22

再看另外一种过河方式:

这种过河方式所花费的时间是:
3 + 2 + 8 + 3 + 3 = 19

比上面那种貌似正确的方式用时更短。

这是怎么造成的呢?
可以看到在第一种方法中第2次过河的是 (2, 7),第3次过河的是(2, 8);第二种方法第2次过河的是(7, 8),第3次过河的是(2, 3)。
第二种方法中,8花了8分钟过河,第一种中8过河也花了8分钟过河;而第二种中7跟着8过河,没有单独过河,这中间就节省了很多时间。虽然(7, 8)过河后要3将船划回对岸花的时间比2将船划回对岸花的时间更长,但是第3次过河时,(2, 3)过河花的时间比(2, 7)少得更多,节约的时间为:7 - 3 - 1 = 3 分钟,方案二比方案一少花3分钟。(有点像田忌赛马的思想一样,(7, 8)一起过河比(2, 8)一起过河能够抵消掉更多的时间) 。

既然不是那种貌似最优的方法得到了最优的结果,那应该怎么分析得到求最短时间的方法呢?肯定是超能的Dynamic Programming了.

设dp[i]表示前i个人过河所花费的最短时间,考虑如下情景:
① 当河边还剩下 1 个人时,即dp[i - 1]已经得到
由河对岸过河时间最短的T[1]将船划回来,然后T[i]和T[1]一起过河,因此过河时间为
dp[i] = dp[i - 1] + T[1] + T[i];
② 当河边还剩下 2 个人时,即dp[i - 2]已经得到
由河对岸过河时间最短的T[1]将船划回来;
设河边剩下的两个人为 j、i,T[j] < T[i];

  • 若下一次 j 和 i不一起过河,而是T[1]和T[j]一起过河得到的整体的过河时间最短,将是上面的那种情况;
    dp[j] = dp[i - 1] = dp[i - 2] + T[1] + T[j];
    dp[i] = dp[i - 1] + T[1] + T[i] = dp[j] + T[1] + T[i]
  • 若下一次 j 和 i 一起过河,然后再由河对岸过河时间最短的人T[2]将船划回,然后T[1]和T[2]一起过河;
    dp[i] = dp[i - 2] + T[1] + T[i] + T[2] + T[2];

由此可的

dp[i] = max(dp[i - 1] + T[1] + T[i], dp[i - 2] + T[1] + T[i] + 2 * T[2])
边界:
dp[1] = T[1]
dp[2] = T[2]

那你只考虑了剩2个人得出了递推公式(也即转移方程),那河边剩下3个人没有过河呢,那有没有可能dp[i]取决于dp[i - 3]呢?
这是不会的,因为船每次只能载2个人,dp[i]的状态最多和dp[i - 2]有关。举例说明的话如下:

举例说明:
根据上面的分析,岸边还剩下2个人的时候,产生的过河组合有:(1, j)(1, i)和(j, i)(1, 2);
假设岸边还剩下3个人:k、j、i,且T[k] < T[j] < T[i],和对岸过河时间最短的 1 将船划回来了,那么岸边有4个人:1、k、j、i,可以分为6中情况:
①1, k先过河,花费时间:
T[k] + T[1] + T[j] + T[1] + T[i]
或者
T[k] + T[1] + T[i] + T[2] + T[2]
②1, j先过河,花费时间:
T[j] + T[1] + T[k] + T[1] + T[i]
或者
T[j] + T[1] + T[i] + T[2] + T[2]
③1, i先过河,花费时间:
T[i] + T[1] + T[k] + T[1] + T[j]
或者
T[i] + T[1] + T[j] + T[2] + T[2]
④k, j先过河,花费时间:
T[j] + T[2] + T[i] + T[1] + T[2]
⑤k, i先过河,花费时间:
T[i] + T[2] + T[j] + T[1] + T[2]
⑥j, i先过河,花费时间:
T[i] + T[2] + T[k] + T[1] + T[2]

消减去相同量,共得到3种结果:
T[k] + T[j] + T[1]
T[k] + T[2] + T[2]
T[j] + T[2] + T[2]
而T[k] < T[k],所以最优结果一定在 T[k] + T[j] + T[1] 和 T[k] + T[2] + T[2]中,而情况①中的去掉相同项得到的就是这两个,因此只考虑情况①就能得到全局最优解了,其他组合花费的时间不可能比情况①花费的时间更小。
而情况①中,可以看到是1, k先过河,再j、i 过河,因此:
dp[i - 3] + ① 就相当于dp[i - 2] + (j、i 的情况)
因此,求dp[i]时不需要考虑dp[i - 3],即dp[i]的状态只与dp[i - 1]和dp[i - 2]有关

代码

Java

public class Main{public static void main(String[] args) {Scanner input = new Scanner(System.in);int T = input.nextInt();for (int k = 0; k < T; k++) {int n = input.nextInt();int[] people = new int[n + 1];for (int j = 1; j <= n; j++) people[j] = input.nextInt();if (n == 2) System.out.println(people[1] + people[2]);Arrays.sort(people);int[] dp = new int[n + 1];dp[1] = people[1];dp[2] = people[2];for (int i = 3; i <= n; i++) {dp[i] = Math.min(dp[i - 1] + people[1] + people[i], dp[i - 2] + people[1] + people[i] + 2 * people[2]);}System.out.println(dp[n]);}}
}
输出
4
2 10 12 11
374
2 7 3 8
19

C++

#include <algorithm>
#include <vector>
#include <iostream>
using namespace std;int main() {int n;cin >> n;vector<int> dp(n + 1, 0);vector<int> T(n + 1, 0);for (int i = 0; i < n; i++) cin >> T[i];sort(T.begin(), T.end());dp[1] = T[1];dp[2] = T[2];for (int i = 3; i <= n; i++) {dp[i] = min(dp[i - 1] + T[1] + T[i], dp[i - 2] + T[1] + T[i] + T[2] * 2);}cout << dp[n] << endl;return 0;
}

[编程题]: 过河问题相关推荐

  1. 字节跳动2019春招第二次笔试编程题

    字节跳动2019春招第二次笔试编程题 1.变身程序员 题目描述 输入描述 输出描述 示例 示例1 示例2 示例3 分析 参考代码 2.特征提取 题目描述 输入描述 输出描述 示例 示例1 备注 分析 ...

  2. [JS] [编程题] 配置文件恢复

    [编程题]配置文件恢复 时间限制:C/C++ 1秒,其他语言2秒空间限制:C/C++ 32M,其他语言64M 链接:https://www.nowcoder.com/questionTerminal/ ...

  3. Linux编程题:信号量同步三进程依次打印若干次ABC

    三个进程依次打印ABC..... 思路及大致流程如下: 思路取自这位大哥: Linux编程题:创建3个线程分别打印abc,用信号量进行同步_cleverlemon的博客-CSDN博客 这位大哥写的是线 ...

  4. c语言编程题餐饮服务打分,求详细分析C语言题餐饮服务质量调查打分题和答案..._质量员考试_帮考网...

    bangsaizhuo 新兵答主 11-09 TA获得超过6761个赞 二.填空题 1. ___变量__是指在程序运行过程中,值可以发生变化的量. 2.C语言是一种____区分_(区分/不区分)字母大 ...

  5. 数据结构面试题编程题_您下次编程面试时应该了解的顶级数据结构

    数据结构面试题编程题 by Fahim ul Haq 通过Fahim ul Haq Niklaus Wirth, a Swiss computer scientist, wrote a book in ...

  6. java在线编程题_阿里笔试题(2017在线编程题)-- 数串分组 --Java实现

    看到有人写了阿里的面试题,心里痒痒,好久 没搞过这些了,写着实现一下 题目 2017年3月阿里在线编程题(实习内推) 给定一串数字 判断是否存在这三个元素,它们将数字串分为四个子串,其中每个子串的数字 ...

  7. JavaScript初学者编程题(25)

    JavaScript初学者编程题(25) 题目:给定一个字符串,请将字符串里的字符按照出现的频率降序进行重新排列并返回 第一种方法,利用对象和数组 JavaScript部分 var str = &qu ...

  8. JavaScript初学者编程题(24)

    JavaScript初学者编程题(24) 题目:给你一个 m x n 的整数网格 accounts ,其中 accounts[i][j] 是第 i 位客户在第 j 家银行托管的资产数量.返回最富有客户 ...

  9. JavaScript初学者编程题(23)

    JavaScript初学者编程题(23) 题目:给你一个字符串 S,请你删去其中的所有元音字母( 'a','e','i','o','u'),并返回这个新字符串 HTMl部分 <input typ ...

最新文章

  1. 宏基因组实战10. 绘制圈图-Circos安装与使用
  2. 去掉一个linux的ip,linux – iptables删除除一个IP之外的所有传入ICMP请求
  3. (转)使用vsphere client 克隆虚拟机
  4. python爬虫scrapy步骤mac系统_python scrapy简单爬虫记录(实现简单爬取知乎)
  5. LuoguP5366 [SNOI2017]遗失的答案
  6. JPA基础(四):第一个JPA实例与JPA主键生成策略
  7. android访问静态内部类,Java 内部类详解
  8. rsync java_Linux 同步工具rsync学习
  9. maven无法找到依赖(手动使用maven导入依赖包解决)
  10. 第三方支付接口开发原理
  11. 图像分割——meanshift算法(C++GDAL库)
  12. 风控违约场景如何预测,来看看这份常见的三种模型实现算法对比
  13. wxpython中表格顶角怎么设置,wxpython listctrl并修复列宽
  14. 应用在智能触摸遥控器中的触摸芯片
  15. 摄像头8mm可以看多远_监控摄像头有多少种,如何来选型
  16. 数学建模 ————统计问题之预测(一)
  17. MyBatis 开发有bug找不到?多看看执行流程
  18. HTML5设定video视频为背景
  19. 分辨hcip和hcnp有什么区别,了解两者的含义
  20. Webug4.0 打靶笔记

热门文章

  1. edge浏览器什么相当于ie的中低_Win10 Edge浏览器和IE浏览器哪个好用?
  2. 企业拥有PMO(项目管理办公室)的好处
  3. 胖葵酒店管理系统(android客户端+javaweb服务端+腾讯云服务器+腾讯云数据库)
  4. 华硕服务器系统都还原不了,windows10系统还原失败怎么办|windows10系统还原失败如何解决...
  5. Rundll32解密
  6. z-index 无效解决方法
  7. 多目标应用:基于NSGAII的环境经济负荷分配(Environmental/Economic power Dispatch,EED)多目标优化(IEEE-118bus)
  8. 如何利用人性的弱点在互联网中找到利润高的项目
  9. #python#模拟登录超星
  10. 什么是cmd?能做啥?告诉你...