jjzjj

华为OD机试 - 士兵过河(Java & JS & Python)

伏城之外 2023-08-01 原文

题目描述

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

  1. 当1个士兵划船过河,用时为 a[i];0 <= i < N
  2. 当2个士兵坐船同时划船过河时,用时为max(a[j],a[i])两士兵中用时最长的。
  3. 当2个士兵坐船1个士兵划船时,用时为 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)两个士兵的同时划船时,如果划速不同则会导致船原地转圈圈;所以为保持两个士兵划速相同,则需要向划的慢的士兵看齐。
2)两个士兵坐船时,重量增加吃水加深,水的阻力增大;同样的力量划船速度会变慢;
3)由于河水湍急大量的力用来抵消水流的阻力,所以2)中过河用时不是a[i] *2,
而是a[i] * 10。

用例

输入5
43
12 13 15 20 50
输出3 40
说明可以达到或小于43的一种方案:
第一步:a[0] a[1] 过河用时:13
第二步:a[0] 返回用时:12
第三步:a[0] a[2] 过河用时:15
输入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
输入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

题目解析

本题是 POJ - 1700 Crossing River_伏城之外的博客-CSDN博客 的变种题。

建议大家先搞定这题,然后再来看本题。

本题在前面这题的基础上,多了一个过河时间限制,以及要求最多存活士兵(即在限制时间内过最多的人)

对于贪心解法,可以结合二分法来求解本题。

即在0~N中尝试找到成功过河的人数,其中0指的是成功过河的人数为0个,N指的是成功过河的人数为N个。

将二分法找到的可能人数mid带入上面POJ-1700的逻辑中,计算出mid个人都过河所需的最短时间need,将need和本题过河时间限制limit进行比较:

  • 若 need > limit,则说明当前mid个人无法成功过河,即过河人数偏多了,我们应该减少过河人数
  • 若 need < limit,则说明当前mid个人可以成功过河,但是可能还可以过更多人数
  • 若 need == limit,则说明当前mid个人刚刚好可以在limit时间过完河,则此时mid就是最多存货的士兵数

对于动态规划解法,由于是从0人过河递推到N人过河,因此不需要二分尝试过河人数,而是可以直接基于dp[i]来实时比较T,如果超过了T,则说明只能过河 i 人,耗时dp[i-1]

另外,本题中说:

当2个士兵坐船1个士兵划船时,用时为 a[i]*10;a[i]为划船士兵用时。

假设x士兵划船用时为a[x],y士兵划船用时为a[y],a[x] < a[y]

这句话的意思是:如果x,y一起划船,有两种过河时间,分别是:

  • a[x] * 10
  • a[y]

如果a[y] > a[x] * 10,我们应该选择a[x] * 10,即让较快的士兵单独划船过河,这样耗时更短。

但是,本题中又说:

(10 < a[i]< 100; 0<= i< N)

  • 10 < a[y] < 100
  • 10 < a[x] < 100

那么必然:100 < a[x] * 10 < 1000

即必然 a[x] * 10 > a[y]

因此,我们不需要考虑上面那种两个士兵坐船,一个士兵划船的情况。

贪心解法

JavaScript算法源码

/* JavaScript Node ACM模式 控制台输入获取 */
const readline = require("readline");

const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout,
});

const lines = [];
rl.on("line", (line) => {
  lines.push(line);

  if (lines.length === 3) {
    const N = lines[0] - 0;
    const T = lines[1] - 0;
    const times = lines[2].split(" ").map(Number);
    console.log(getResult(N, T, times));
    lines.length = 0;
  }
});

/**
 *
 * @param {*} n 士兵数
 * @param {*} limit 过河时间上限
 * @param {*} times 数组,元素表示每个士兵的过河时长
 * @return {*} ”最多存活士兵数” “最短用时”
 */
function getResult(n, limit, times) {
  // 过河时间升序
  times.sort((a, b) => a - b);

  // 最少成功过河人数
  let min = 0;
  // 最多成功过河人数
  let max = n;

  // 记录题解
  let ans = "";

  // 二分法取可能成功的过河人数
  while (min <= max) {
    // mid是过河人数
    const mid = Math.floor((min + max) / 2);
    // 计算mid个人过河所需的最短时间need
    const need = getMinCrossRiverTime(mid, times.slice(0, mid));

    // 如果need超过了过河时间上限limit,那么说明能成功过河的人没这么多
    if (need > limit) {
      max = mid - 1;
    } else if (need < limit) {
      // 如果need小于过河时间上限limit,那么说明mid个最快的人可以在limit时间内成功过河
      ans = `${mid} ${need}`;
      // 但是可能还可以过更多人
      min = mid + 1;
    } else {
      // 如果need == limit,那么说明过河人数刚好可以在limit时间内成功过河,此时可以直接返回
      ans = `${mid} ${need}`;
      break;
    }
  }

  return ans;
}

// 计算n个人运到河对岸所需要花费的最少时间
function getMinCrossRiverTime(n, t) {
  let cost = 0;

  while (n > 0) {
    if (n == 1) {
      cost += t[0];
      break;
    } else if (n == 2) {
      cost += t[1];
      break;
    } else if (n == 3) {
      cost += t[1] + t[0] + t[2];
      break;
    } else {
      cost += Math.min(
        t[n - 1] + t[0] + t[n - 2] + t[0],
        t[1] + t[0] + t[n - 1] + t[1]
      );
      n -= 2;
    }
  }

  return cost;
}

Java算法源码

import java.util.Arrays;
import java.util.Scanner;

public class Main {
  public static void main(String[] args) {
    Scanner sc = new Scanner(System.in);

    int n = sc.nextInt();
    int t = sc.nextInt();

    int[] times = new int[n];

    for (int i = 0; i < n; i++) {
      times[i] = sc.nextInt();
    }

    System.out.println(getResult(n, t, times));
  }

  /**
   * @param n 士兵数
   * @param limit 过河时间上限
   * @param times 数组,元素表示每个士兵的过河时长
   * @return ”最多存活士兵数” “最短用时”
   */
  public static String getResult(int n, int limit, int[] times) {
    // 过河时间升序
    Arrays.sort(times);

    // 最少成功过河人数
    int min = 0;
    // 最多成功过河人数
    int max = n;

    // 记录题解
    String ans = "";

    // 二分法取可能成功的过河人数
    while (min <= max) {
      // mid是过河人数
      int mid = (min + max) / 2;
      // 计算mid个人过河所需的最短时间need
      int need = getMinCrossRiverTime(mid, Arrays.copyOfRange(times, 0, mid));

      // 如果need超过了过河时间上限limit,那么说明能成功过河的人没这么多
      if (need > limit) {
        max = mid - 1;
      } else if (need < limit) {
        // 如果need小于过河时间上限limit,那么说明mid个最快的人可以在limit时间内成功过河
        ans = mid + " " + need;
        // 但是可能还可以过更多人
        min = mid + 1;
      } else {
        // 如果need == limit,那么说明过河人数刚好可以在limit时间内成功过河,此时可以直接返回
        ans = mid + " " + need;
        break;
      }
    }

    return ans;
  }

  // 计算将n个人运到河对岸所需要花费的最少时间
  public static int getMinCrossRiverTime(int n, int[] t) {
    int cost = 0;

    while (n > 0) {
      if (n == 1) {
        cost += t[0];
        break;
      } else if (n == 2) {
        cost += t[1];
        break;
      } else if (n == 3) {
        cost += t[1] + t[0] + t[2];
        break;
      } else {
        cost += Math.min(t[n - 1] + t[0] + t[n - 2] + t[0], t[1] + t[0] + t[n - 1] + t[1]);
        n -= 2;
      }
    }

    return cost;
  }
}

Python算法源码

# 输入获取
n = int(input())
t = int(input())
times = list(map(int, input().split()))


# 计算n个人运到河对岸所需要花费的最少时间
def getMinCrossRiverTime(n, t):
    cost = 0

    while n > 0:
        if n == 1:
            cost += t[0]
            break
        elif n == 2:
            cost += t[1]
            break
        elif n == 3:
            cost += t[1] + t[0] + t[2]
            break
        else:
            cost += min(t[n - 1] + t[0] + t[n - 2] + t[0], t[1] + t[0] + t[n - 1] + t[1])
            n -= 2

    return cost


# 算法入口
def getResult(n, limit, times):
    """
    :param n: 士兵数
    :param limit: 过河时间上限
    :param times: 数组,元素表示每个士兵的过河时长
    :return: ”最多存活士兵数” “最短用时”
    """
    times.sort()

    # 最少成功过河人数
    low = 0
    # 最多成功过河人数
    high = n

    # 记录题解
    ans = ""

    # 二分法取可能成功的过河人数
    while low <= high:
        # mid是过河人数
        mid = (low + high) // 2
        # 计算mid个人过河所需的最短时间need
        need = getMinCrossRiverTime(mid, times[:mid])

        # 如果need超过了过河时间上限limit,那么说明能成功过河的人没这么多
        if need > limit:
            high = mid - 1
        elif need < limit:
            # 如果need小于过河时间上限limit,那么说明mid个最快的人可以在limit时间内成功过河
            ans = f"{mid} {need}"
            # 但是可能还可以过更多人
            low = mid + 1
        else:
            # 如果need == limit,那么说明过河人数刚好可以在limit时间内成功过河,此时可以直接返回
            ans = f"{mid} {need}"
            break

    return ans


# 算法调用
print(getResult(n, t, times))

动态规划解法(100%通过率)

Java算法源码

import java.util.Arrays;
import java.util.Scanner;

public class Main {
  public static void main(String[] args) {
    Scanner sc = new Scanner(System.in);

    int n = sc.nextInt();
    int T = sc.nextInt();

    int[] times = new int[n];

    for (int i = 0; i < n; i++) {
      times[i] = sc.nextInt();
    }

    System.out.println(getResult(n, T, times));
  }

  /**
   * @param n 士兵数
   * @param limit 过河时间上限
   * @param t 数组,元素表示每个士兵的过河时长
   * @return ”最多存活士兵数” “最短用时”
   */
  public static String getResult(int n, int limit, int[] t) {
    // 过河时间升序
    Arrays.sort(t);

    int[] dp = new int[n];

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

      if (dp[i] > limit) return i + " " + dp[i - 1];
    }

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

JavaScript算法源码

/* JavaScript Node ACM模式 控制台输入获取 */
const readline = require("readline");

const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout,
});

const lines = [];
rl.on("line", (line) => {
  lines.push(line);

  if (lines.length === 3) {
    const N = lines[0] - 0;
    const T = lines[1] - 0;
    const times = lines[2].split(" ").map(Number);
    console.log(getResult(N, T, times));
    lines.length = 0;
  }
});

/**
 *
 * @param {*} n 士兵数
 * @param {*} limit 过河时间上限
 * @param {*} t 数组,元素表示每个士兵的过河时长
 * @return {*} ”最多存活士兵数” “最短用时”
 */
function getResult(n, limit, t) {
  // 过河时间升序
  t.sort((a, b) => a - b);

  const dp = new Array(n);

  for (let i = 0; i < n; i++) {
    if (i == 0) {
      dp[0] = t[0];
      if (dp[0] > limit) return "0 0";
    } else if (i == 1) {
      dp[1] = t[1];
    } else {
      dp[i] = Math.min(
        dp[i - 1] + t[i] + t[0],
        dp[i - 2] + t[0] + t[i] + t[1] + t[1]
      );
    }

    if (dp[i] > limit) return i + " " + dp[i - 1];
  }

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

Python算法源码

# 输入获取
n = int(input())
T = int(input())
times = list(map(int, input().split()))


# 算法入口
def getResult(n, limit, t):
    """
    :param n: 士兵数
    :param limit: 过河时间上限
    :param t: 数组,元素表示每个士兵的过河时长
    :return: ”最多存活士兵数” “最短用时”
    """
    times.sort()

    dp = [0] * n

    for i in range(n):
        if i == 0:
            dp[0] = t[0]
            if dp[0] > limit:
                return "0 0"
        elif i == 1:
            dp[1] = t[1]
        else:
            dp[i] = min(dp[i - 1] + t[i] + t[0], dp[i - 2] + t[0] + t[i] + t[1] + t[1])

        if dp[i] > limit:
            return str(i) + " " + str(dp[i - 1])

    return str(n) + " " + str(dp[n - 1])


# 算法调用
print(getResult(n, T, times))

有关华为OD机试 - 士兵过河(Java & JS & Python)的更多相关文章

  1. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

  2. ruby-on-rails - rails : "missing partial" when calling 'render' in RSpec test - 2

    我正在尝试测试是否存在表单。我是Rails新手。我的new.html.erb_spec.rb文件的内容是:require'spec_helper'describe"messages/new.html.erb"doit"shouldrendertheform"dorender'/messages/new.html.erb'reponse.shouldhave_form_putting_to(@message)with_submit_buttonendendView本身,new.html.erb,有代码:当我运行rspec时,它失败了:1)messages/new.html.erbshou

  3. ruby-on-rails - 由于 "wkhtmltopdf",PDFKIT 显然无法正常工作 - 2

    我在从html页面生成PDF时遇到问题。我正在使用PDFkit。在安装它的过程中,我注意到我需要wkhtmltopdf。所以我也安装了它。我做了PDFkit的文档所说的一切......现在我在尝试加载PDF时遇到了这个错误。这里是错误:commandfailed:"/usr/local/bin/wkhtmltopdf""--margin-right""0.75in""--page-size""Letter""--margin-top""0.75in""--margin-bottom""0.75in""--encoding""UTF-8""--margin-left""0.75in""-

  4. ruby-on-rails - 'compass watch' 是如何工作的/它是如何与 rails 一起使用的 - 2

    我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t

  5. ruby-on-rails - 如何从 format.xml 中删除 <hash></hash> - 2

    我有一个对象has_many应呈现为xml的子对象。这不是问题。我的问题是我创建了一个Hash包含此数据,就像解析器需要它一样。但是rails自动将整个文件包含在.........我需要摆脱type="array"和我该如何处理?我没有在文档中找到任何内容。 最佳答案 我遇到了同样的问题;这是我的XML:我在用这个:entries.to_xml将散列数据转换为XML,但这会将条目的数据包装到中所以我修改了:entries.to_xml(root:"Contacts")但这仍然将转换后的XML包装在“联系人”中,将我的XML代码修改为

  6. ruby - 检查 "command"的输出应该包含 NilClass 的意外崩溃 - 2

    为了将Cucumber用于命令行脚本,我按照提供的说明安装了arubagem。它在我的Gemfile中,我可以验证是否安装了正确的版本并且我已经包含了require'aruba/cucumber'在'features/env.rb'中为了确保它能正常工作,我写了以下场景:@announceScenario:Testingcucumber/arubaGivenablankslateThentheoutputfrom"ls-la"shouldcontain"drw"假设事情应该失败。它确实失败了,但失败的原因是错误的:@announceScenario:Testingcucumber/ar

  7. ruby-on-rails - Rails 3.2.1 中 ActionMailer 中的未定义方法 'default_content_type=' - 2

    我在我的项目中添加了一个系统来重置用户密码并通过电子邮件将密码发送给他,以防他忘记密码。昨天它运行良好(当我实现它时)。当我今天尝试启动服务器时,出现以下错误。=>BootingWEBrick=>Rails3.2.1applicationstartingindevelopmentonhttp://0.0.0.0:3000=>Callwith-dtodetach=>Ctrl-CtoshutdownserverExiting/Users/vinayshenoy/.rvm/gems/ruby-1.9.3-p0/gems/actionmailer-3.2.1/lib/action_mailer

  8. ruby-on-rails - 如何优雅地重启 thin + nginx? - 2

    我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server

  9. ruby - 在 jRuby 中使用 'fork' 生成进程的替代方案? - 2

    在MRIRuby中我可以这样做:deftransferinternal_server=self.init_serverpid=forkdointernal_server.runend#Maketheserverprocessrunindependently.Process.detach(pid)internal_client=self.init_client#Dootherstuffwithconnectingtointernal_server...internal_client.post('somedata')ensure#KillserverProcess.kill('KILL',

  10. ruby - 主要 :Object when running build from sublime 的未定义方法 `require_relative' - 2

    我已经从我的命令行中获得了一切,所以我可以运行rubymyfile并且它可以正常工作。但是当我尝试从sublime中运行它时,我得到了undefinedmethod`require_relative'formain:Object有人知道我的sublime设置中缺少什么吗?我正在使用OSX并安装了rvm。 最佳答案 或者,您可以只使用“require”,它应该可以正常工作。我认为“require_relative”仅适用于ruby​​1.9+ 关于ruby-主要:Objectwhenrun

随机推荐