【华为OD机试】士兵过河

士兵过河

题目描述

一只N个士兵的军队正在趁夜色逃亡,途中遇到一条湍急的大河。
敌军在T的时长后到达河面,没到过对岸的士兵都会被消灭。
现在军队只找到了一只小船,这船最多能同时坐上2个士兵。

1)当一个士兵划船过河,用时为a[i];0 <= i < N
2)当两个士兵坐船同时划船过河时,用时为 max(a[j], a[i]) 两士兵中用时最长的。
3)当两个士兵坐船一个士兵划船时,用时为 a[i] * 10 ; a[i] 为划船士兵用时。
4)如果士兵下河游泳,则会被湍急水流直接带走,算作死亡。

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

输入描述

第一行:N 表示士兵数(0 < N < 1,000,000)
第二行:T 表示敌军到达时长(0 < T < 100,000,000)
第三行:a[0] a[1] … a[i] … a[N - 1]
a[i] 表示每个士兵的过河时长。
(10 < a[i] < 100; 0 < i < N)

输出描述

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

测试用例

用例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

用例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

备注

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

解题思路

  1. 小船容量 2 人;
  2. 两人过河最短过河时间为 a[i] * 10 与 max(a[i], a[j]) 中的最小值;
  3. 需要士兵存活最多,可根据士兵渡河时间由小到大排序,若排序后过河时间短的士兵无法存活,则时间长的士兵可以不考虑存活;
  4. 要求士兵总过河时间最短;

就士兵 a[i] 进行过河假设,若需要 a[i] 渡河,则 a[i - 1] 可能已过河或待过河(小船容量为 2,且按过河时间由小到大排序),同时 小船、a[i - 2]及之前的士兵在对岸(如果小船不在对岸,需要有人将小船送回);

那么士兵 a[i] 渡河后总时间就归于两种情况:
1)a[i - 1] 未渡河,需要 a[0] 将船划回,a[i - 1] a[i] 过河后 a[1] 划回,最终 a[0] a[1] 过河

T i = T i − 2 + a [ 0 ] + m i n ( a [ i − 1 ] ∗ 10 , a [ i ] ) + a [ 1 ] + m i n ( a [ 0 ] ∗ 10 , a [ 1 ] ) T_i = T_{i - 2} + a[0] + min(a[i - 1] * 10, a[i]) + a[1] + min(a[0] * 10, a[1]) Ti=Ti2+a[0]+min(a[i1]10,a[i])+a[1]+min(a[0]10,a[1])

2)a[i - 1] 已渡河,需要 a[0] 将船划回,a[0] a[i] 过河

T i = T i − 1 + a [ 0 ] + m i n ( a [ 0 ] ∗ 10 , a [ i ] ) T_i = T_{i - 1} + a[0] + min(a[0] * 10, a[i]) Ti=Ti1+a[0]+min(a[0]10,a[i])

示意图如下

A B a[i-1] 已过河 a[i - 1] a[0] a[0] a[i - 1] a[i-1]待过河 a[i - 2] a[0] a[i - 1] a[i] a[1] a[0] a[1] par A B

代码示例

/**
 * 获取士兵过河最大存活人数及最短过河时间
 * @param n 士兵数 (0 < N < 1,000,000)
 * @param t 敌军到达时长 (0 < T < 100,000,000)
 * @param a a[i]表示每个士兵的过河时长。 (10 < a[i] < 100; 0<= i < N)
 * @return 存活士兵数 过河时间
 */
public String crossTheRiver(int n, int t, int[] a) {
    Arrays.sort(a);
    if(a[0] > t) {
        return "0 0";
    }
    // 士兵数小于 2
    if(n < 2) {
        return "1 " + a[0];
    }
    
    int[] dp = new int[n];
    dp[0] = a[0];
    dp[1] = minTime(a[0], a[1]);
    if(dp[1] > t) {
        return "1 " + dp[0];
    }

    for (int i = 2; i < n; i++) {
        int a1 = dp[i - 1] + a[0] + minTime(a[0], a[i]);
        int a2 = dp[i - 2] + a[0] + minTime(a[i - 1], a[i]) + a[1] + minTime(a[0], a[1]);
        dp[i] = Math.min(a1, a2);
        if(dp[i] > t) return i + " " + dp[i - 1];
    }

    return n + " " + dp[n - 1];
}

/**
 * 最短渡河时间
 * @param t1 时间1
 * @param t2 时间2
 * @return 最短时间
 */
private int minTime(int t1, int t2) {
    return Math.min(t1 * 10, t2);
}

  • 5
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值