题目描述
一支个士兵的军队正在趁夜色逃亡 途中遇到一条湍急的大河敌军在的时长后到达河面, 没到过对岸的士兵都会被消灭。
现在军队只找到了只小船,这船最多能同时坐上个士兵。
- 当个土兵划船过河,用时为
- 当个士兵坐船同时划船过河时,用时为两土兵中用时最长的.
- 当个士兵坐船个士兵划船时,用时为 为划船士兵用时
- 如果士兵下河游泳,则会被湍急水流直接带走,算作死亡
请帮忙给出一种解决方案,保证存活的士兵最多,且过河用时最短.
输入描述
第一行:表示士兵数 第二行:表示敌军的到达时长 第三行:
- 表示每一个士兵的过河时长
输出描述
第一行:“最多存活的士兵数” “最短用时”
备注
- 两个士兵的同时划船时,如果划速不同则会导致船原地转圈圈;所以为保持两个士兵划速相同,则需要向划的慢的士兵看齐
- 两个士兵坐船时,重量增加吃水加深,水的阻力增大,同样的力量划船速度会变慢
- 由于河水湍急大量的力用来抵消水流的阻力,所以 条件2 中过河用时不是 而是
用例
- 用例1
--输入
5
43
12 13 15 20 50
--输出
3 40
--说明
可以达到或小于43的一种方案
第一步: a[0] a[1]过河用时: 13
第二步: a[0] 返回用时: 12
第三步: a[0] a[2]过河用时: 15
- 用例2
--输入
5
130
50 12 13 15 20
--输出
5 128
--说明
可以达到或小于130的一种方案:
第一步: a[1] a[2] 过河用时: 13
第二步: a[1] 返回用时:12
第三步: a[0] a[4]过河用时: 50
第四步: a[2] 返回用时: 13
第五步: a[1] a[2] 过河用时: 13
第六步: a[1] 返回用时:12
第七步: a[1] a[3] 过河用时:15
所以输出为:
5 128
- 用例3
--输入
7
171
25 12 13 15 20 35 20
--输出
7 171
--说明
可以达到或小于 171 的一种方案:
第一步: a[1] a[2] 过桥用时: 13
第二步: a[1] 带火把返回用时: 12
第三步: a[0] a[5] 过桥用时: 35
第四步: a[2] 带火把返回用时: 13
第五步: a[1] a[2] 过桥用时: 13
第六步: a[1] 带火把返回用时: 12
第七步: a[4] a[6] 过桥用时: 20
第八步: a[2] 带火把返回用时: 13
第九步: a[1] a[3] 过桥用时: 15
第十步: a[1] 带火把返回用时: 12
第十一步: a[1] a[2] 过桥用时: 13
所以输出为:
7 171
code show + analysis
package com.hw;
import java.util.Arrays;
import java.util.Scanner;
/**
* desc : 士兵过河.
* <p> <a href="https://fcqian.blog.csdn.net/article/details/128325033">...</a>
* create time : 2023/3/14 18:16
*/
public class SoldierRiver {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
while(in.hasNextInt()) {
int N = in.nextInt();
int T = in.nextInt();
int[] time = new int[N];
for (int i = 0; i < N; i++) {
time[i] = in.nextInt();
}
soldierCrossRiver(N, T, time);
}
}
/*
* 根据题目意思,那么一共有两种划船方案
* |- 一个人坐船,一个人划船,用时为 a[i] * 10 -- 即划船的人所用时长 * 10
* |- 两个人同时划船,用时为 max(a[i], a[j])
*
* ----那么首先,可以将所有士兵的划船时间进行排序.
*
* 记划船最快的前两个士兵为 士兵 A、B
* 那么
* 方案一:A+B 过河 A 返回 A 再带第三个人过河
* 方案二:B带第三个人过河 B 返回 A 带第四个人过河 A 返回 A+B
*
* 即 最佳过河方案在 A B 不断返回的过程中得到
*
* 适合用动态规划求解
* 1 个士兵过河,最快的直接划船过去
* 2 个士兵过河,划船 第一快和第二快 的士兵一起划船过去
* 3 个士兵过河,第一和第二 先过河,第一回来,再带一个人过河
* 4 个士兵过河:有两种方案
* A B 先过去,A 回来,3 4过去,B 回来,A B 再过去
* 3 个人已经过去了,A 回来,带一个人过去
*/
private static void soldierCrossRiver(int N, int T, int[] time) {
Arrays.sort(time);
// 划船最快的士兵
int time0 = time[0];
// 划船第二快的士兵
int time1 = time[1];
if(time0 > T) {
// 一个都过不去河
System.out.print(0 + " " + 0);
}
// dp[i] 表示第 i 个人过河所用的时间
int[] dp = new int[N + 1];
dp[1] = time0;
// 两个人一起划船过河,取一个最佳方案.
dp[2] = getTime(time0, time1);
boolean flag = true;
for (int i = 3; i <= N; i++) {
// 方案一:i - 1 个人过河的时间 + 最快的人回来的时间 + 带当前人去的时间.
int planA = dp[i - 1] + time0 + getTime(time0, time[i - 1]);
// 方案二:i - 2 个人过河的时间 + 第二块的人回来的时间 + 当前人和前一个人去的时间 + 第一快的人去的时间
int planB = dp[i - 2] + time0 + getTime(time[i - 2], time[i - 1]) + time1 + dp[2];
dp[i] = Math.min(planA, planB);
if(dp[i] > T) {
flag = false;
// 这里表示只能过去 i - 1 个人了
System.out.print((i - 1) + " " + dp[i - 1]);
break;
}
}
if(flag) {
System.out.print(N + " " + dp[N]);
}
}
// timeA < timeB
private static int getTime(int timeA, int timeB) {
return Math.min(timeA * 10, timeB);
}
}
标签:13,过河,int,用时,划船,士兵
From: https://blog.51cto.com/u_16079703/6991057