题目描述

一支士兵过河_用例个士兵的军队正在趁夜色逃亡 途中遇到一条湍急的大河敌军在士兵过河_java_02的时长后到达河面, 没到过对岸的士兵都会被消灭。

现在军队只找到了士兵过河_System_03只小船,这船最多能同时坐上士兵过河_System_04个士兵。

  1. 士兵过河_System_03个土兵划船过河,用时为 士兵过河_用例_06
  2. 士兵过河_System_04个士兵坐船同时划船过河时,用时为士兵过河_用例_08两土兵中用时最长的.
  3. 士兵过河_System_04个士兵坐船士兵过河_System_03个士兵划船时,用时为 士兵过河_System_11 士兵过河_java_12为划船士兵用时
  4. 如果士兵下河游泳,则会被湍急水流直接带走,算作死亡

请帮忙给出一种解决方案,保证存活的士兵最多,且过河用时最短.

输入描述

第一行:士兵过河_用例表示士兵数士兵过河_System_14 第二行:士兵过河_java_02表示敌军的到达时长士兵过河_java_16 第三行:士兵过河_用例_17

  • 士兵过河_java_12表示每一个士兵的过河时长
  • 士兵过河_用例_19

输出描述

第一行:“最多存活的士兵数” “最短用时”

备注

  1. 两个士兵的同时划船时,如果划速不同则会导致船原地转圈圈;所以为保持两个士兵划速相同,则需要向划的慢的士兵看齐
  2. 两个士兵坐船时,重量增加吃水加深,水的阻力增大,同样的力量划船速度会变慢
  3. 由于河水湍急大量的力用来抵消水流的阻力,所以 条件2 中过河用时不是 士兵过河_System_20而是士兵过河_System_11

用例

  • 用例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);
    }

}