ACM模式

华为OD机考是基于牛客平台进行的,且必须采用ACM模式

ACM模式:

  • 机试系统调用你的代码时,传入的都是字符串,题目的输入描述会说明字符串的组成规则,你需要根据这些规则从输入字符串中解析出需要的数据。
  • 当算法程序计算出结果后,也不能直接返回给机试系统,因为机试系统只接收字符串,我们还需要根据题目的输出描述,来生成要求格式的字符串返回。

Java语言的ACM处理

Scanner对象创建,以及next、nextLine方法

Java语言最常用的输入获取方式就是使用java.util.Scanner类,通常情况下,我们会使用Scanner类来获取控制台输入,因此在创建Scanner对象时,会传入控制台输入流System.in

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

Scanner对象有两种方式获取控制台输入的字符串信息:next和nextLine,两种方式的区别是:

  • nextLine是按行截取,即将换行符作为nextLine输入获取的截止符
  • next是按空格截取,即将空格作为next输入获取的截止符

useDelimiter指定next方法的输入获取截止符

当然,我们使用Scanner对象的useDelimiter方法来自定义next输入获取的截止符

如上面代码,由于next输入获取截止符被变更为了逗号,因此next输入获取只有遇到逗号时,才会停止获取。

但是大部分时候,我们需要截止符是多个,比如同时识别逗号和换行为截止符,此时useDelimiter可以传参一个正则字符串

了解Scanner的useDelimiter方法自定义截止符其实非常有用,比如看下面示例:

输入描述:

第一行输入整数n,表示第二行输入的整数的个数

第二行输入n个整数,以逗号分隔

输入实例:

5
1,2,3,4,5

import java.util.Arrays;
import java.util.Scanner;public class Main {public static void main(String[] args) {Scanner sc = new Scanner(System.in);sc.useDelimiter("[,\n]");int n = sc.nextInt();int[] arr = new int[n];for (int i = 0; i < n; i++) arr[i] = sc.nextInt();System.out.println(Arrays.toString(arr));}
}

上面代码中,如果我们已经知道了输入数据对应的类型,比如是一个整数,则此时如果用next获取的话,后期还要使用Integer.parseInt进行转类型,非常不方便,因此Scanner对象还提供很多nextXxx方法,其中Xxx就是输入数据对应的实际数据类型,通常有nextint,nextDouble,nextLong等。

nextLine使用的一点小坑

在某些情况下,我们不得不使用nextLine来获取一行输入,比如下面例子:

输入描述:

第一行输入一个整数
第二行输入多个整数,以空格分隔

需求:

将第二行输入的整数中比第一行输入整数大的过滤出来

输入示例:

2
1 2 3 4 5

上面例子由于不知道第二行具体会输入多少个整数,因此我们只能按行获取,因此代码如下

import java.util.Scanner;public class Main {public static void main(String[] args) {Scanner sc = new Scanner(System.in);int target = sc.nextInt();String[] line = sc.nextLine().split(" ");for (String s : line) {if (Integer.parseInt(s) > target) {System.out.print(s + " ");}}}
}

大家可以看下上面代码是不是非常正常,没有任何毛病,但是实际运行时

what ? 这是什么问题?这里先不说原因,我们先稍作改动,让代码跑起来

问题其实就是出现在上画红框的地方。

具体原因和解决办法可以参考:在JAVA中Scanner中的next()和nextLine()为什么不能一起使用? - 知乎 (zhihu.com)

为什么需要使用BufferedReader获取输入

Scanner对象获取输入非常方便,但是非常地低效,原因是:

  • Java.util.Scanner类是一个简单的文本扫描类,它可以解析基本数据类型和字符串,它本质上其实是使用正则表达式去读取不同的数据类型
  • Scanner的缓冲区大小为1KB

Scanner获取大数据量的输入时,是非常耗时的,此时极有可能造成算法程序的超时。因此,我们需要一种更加高效的输入获取方式:BufferedReader:

  • Java.io.BufferedReader类为了能够高效的读取字符序列,从字符输入流和字符缓冲区读取文本
  • BufferedReader的缓冲区大小为8KB

BufferedReader的基本使用

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;public class Main {public static void main(String[] args) throws IOException {BufferedReader br = new BufferedReader(new InputStreamReader(System.in));String line = br.readLine();System.out.println(line);}
}
  1. System.in 字节输入流
  2. new InputStreamReader(System.in) 将 字节流 转换为 字符流
  3. new BufferedReader(new InputStreamReader(System.in) ) 将字符流放入字符流缓冲区之中

我们可以通过readLine方法来获取一行输入,注意该方法会抛出异常,我们需要捕获异常进行处理,或者接着抛。

比较困难的输入解析

在华为OD机试中,有一类比较蛋疼的输入描述,那么就是输入的是数组或集合格式的字符串,比如

华为OD机试 - 处理器问题(Java & JS & Python)_伏城之外的博客-CSDN博客

import java.util.Scanner;public class Main {public static void main(String[] args) {Scanner sc = new Scanner(System.in);String line = sc.nextLine(); // [0, 1, 4, 5, 6, 7]String tmpLine = line.substring(1, line.length() - 1); // 0, 1, 4, 5, 6, 7String[] sArr = tmpLine.split(", ");int[] arr = new int[sArr.length];for (int i = 0; i < sArr.length; i++) {arr[i] = Integer.parseInt(sArr[i]);}}
}

或者:华为OD机试 - 连接器问题(Java & JS & Python)_伏城之外的博客-CSDN博客

import java.util.Scanner;public class Main {public static void main(String[] args) {Scanner sc = new Scanner(System.in);String line = sc.nextLine(); // [1,10],[15,20],[18,30],[33,40]String tmpLine = line.substring(1, line.length() - 1); // 1,10],[15,20],[18,30],[33,40String[] sArr = tmpLine.split("],\\[");int[][] arr = new int[sArr.length][2];for (int i = 0; i < sArr.length; i++) {String[] split = sArr[i].split(",");arr[i][0] = Integer.parseInt(split[0]);arr[i][1] = Integer.parseInt(split[1]);}}
}

以及:华为OD机试 - 创建二叉树(Java & JS & Python)_伏城之外的博客-CSDN博客

import java.util.Scanner;public class Main {public static void main(String[] args) {Scanner sc = new Scanner(System.in);String line = sc.nextLine(); // [[0, 0], [0, 0], [1, 1], [1, 0], [0, 0]]String tmpLine = line.substring(2, line.length() - 2); // 0, 0], [0, 0], [1, 1], [1, 0], [0, 0String[] sArr = tmpLine.split("], \\[");int[][] arr = new int[sArr.length][2];for (int i = 0; i < sArr.length; i++) {String[] split = sArr[i].split(", ");arr[i][0] = Integer.parseInt(split[0]);arr[i][1] = Integer.parseInt(split[1]);}}
}

当然如果你学过Java的流式编程,上面代码可以得到极大的简化

import java.util.Arrays;
import java.util.Scanner;public class Main {public static void main(String[] args) {Scanner sc = new Scanner(System.in);//    String line = sc.nextLine(); // [[0, 0], [0, 0], [1, 1], [1, 0], [0, 0]]//    String tmpLine = line.substring(2, line.length() - 2); // 0, 0], [0, 0], [1, 1], [1, 0],// [0, 0//    String[] sArr = tmpLine.split("], \\[");////    int[][] arr = new int[sArr.length][2];//    for (int i = 0; i < sArr.length; i++) {//      String[] split = sArr[i].split(", ");//      arr[i][0] = Integer.parseInt(split[0]);//      arr[i][1] = Integer.parseInt(split[1]);//    }String line = sc.nextLine();Integer[][] arr =Arrays.stream(line.substring(2, line.length() - 2).split("], \\[")).map(s -> Arrays.stream(s.split(", ")).map(Integer::parseInt).toArray(Integer[]::new)).toArray(Integer[][]::new);}
}

输出处理

ACM输出需要按照“输出描述”指定的格式进行输出字符串的组装。这里建议使用StringBuilder和StringJoiner。

其中StringBuilder的append相较于String的字符串+拼接操作在效率和内存上都更好。

而StringJoiner类更多用于有分隔符的字符串拼接,比如下面需求:

将一组数以数组形式输出

如上面代码,StringJoiner构造函数有三个参数,依次是:

  • 分隔符
  • 前缀
  • 后缀

对于一维数组对象而言,可以调用Arrays.toString方法可以达到相同效果

Arrays.toString的底层源码实现

对于集合类对象而言,可以直接调用其toString方法也能达到同样效果

我们可以看下对应toString方法底层源码实现

JavaScript语言(node)的ACM处理

基于line事件监听的输入获取

在node环境下,我们可以引入内置模块readline,该模块可以从输入流按行读取数据。

下面代码中process.stdin是命令行标准输入流

/* JavaScript Node ACM模式 控制台输入获取 */
const readline = require("readline");const rl = readline.createInterface({input: process.stdin,
});rl.on("line", (line) => {console.log(line);
});

监听line事件(on)后,每当从输入流中读取新行时,该模块底层就会触发事件(emit),并且将获取到新行字符串作为参数传入事件绑定的回调函数callback中。如上面代码所示。

node这种基于事件监听的方式获取输入,是一种异步的输入获取方式,这会导致每行输入之间失去了联系,即回调函数内部并不知道当前获取的新行输入是哪一行?

因此,我们通常会定义一个全局变量lines数组,将每次监听获得的输入行缓存进lines,这样lines数组每个元素的序号就是对应的行数,lines数组的长度就是一共输入了几行。

比如下面输入要求:

输入描述:

第一行输入整数n,表示第二行输入的整数的个数

第二行输入n个整数,以逗号分隔

输入实例:

5
1,2,3,4,5

/* JavaScript Node ACM模式 控制台输入获取 */
const readline = require("readline");const rl = readline.createInterface({input: process.stdin,
});const lines = [];
rl.on("line", (line) => {lines.push(line);if (lines.length == 2) {const n = parseInt(lines[0]);const arr = lines[1].split(",").map(Number);console.log(n);console.log(arr);lines.length = 0;}
});

上面需求,由于一共会输入两行,因此当lines数组长度达到2时,就说明当前用例已经输入完成,其中lines[0]就是第一行输入,lines[1]就是第二行输入,我们只要根据要求进行字符串解析就可。

另外,在回调函数的最后,lines.length = 0 的目的时清空lines数组,这样就能进行多组用例的测试了,原因是:当下次新行输入时,lines.length 又等于0了,即进行新一轮的用例输入了。

Promise串行的异步的输入获取

大家可以看下这题华为OD机试 - 高效的任务规划(Java & JS & Python)_伏城之外的博客-CSDN博客

数组样式的输入字符串的解析

如果输入的字符串是标准的数组样式,那么直接可以使用JSON.parse转化为数组,比如

一维数组样式:

二维数组样式:

非标准二维数组样式:

我们可以给他加上前缀"["和后缀”]“来变为标准二维数组样式,然后再用JSON.parse解析

输出处理

JS的输出相较于Java而言就非常容易了,因为JS有模板字符串,另外JS数组的join方法也可以实现使用相同分隔符连接数组元素。

Python语言的ACM处理

Python语言的输入输出处理,相较于Java和JS来说是最简单的。

Python的输入获取,不需要引入额外的库,直接使用内置的input()函数即可获取到控制台的一行输入

后面对于输入行的解析工作,就看Python基本功了。

对于列表样式的输入,我们可以使用eval函数进行转化,例如:

而对于非标准的列表样式字符串,可以先转为标准的,在用eval转

Python的输出处理也非常简单,可以利用模板字符串f"",或者对于有分隔符要求的列表打印,可以使用join内置函数

华为OD机试ACM输入输出相关推荐

  1. 华为od机试(适用B卷),独家整理 已参加机试人员的实战技巧

    华为 OD 机试过程中,都会碰到哪些问题呢,橡皮擦今天为大家整理一下,希望对即将参加机试的[你]有所帮助. 这个是一系列关于华为 OD 的各种问题,你也可以在评论区提问 每篇博客精选 OD 参与者的 ...

  2. 【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南

    本篇博客为大家系统全面的介绍华为 od 机试所有内容,其包括如下知识点. 华为 od 机试题目 华为 od 机试流程 华为 od 机试题型分析 华为 od 机试经验分享 华为 od 机试常见问题解答 ...

  3. 【华为OD机试真题 python】连续出牌数量【2022 Q4 | 200分】

    前言 <华为OD笔试真题 python> 专栏含华为OD机试真题.华为面试题.牛客网华为专栏真题. 如果您正在准备华为的面试,或者华为od的机会,有任何想了解的可以私信我进行交流.我会尽可 ...

  4. 华为OD机试 - 按身高和体重排队(Java) | 机试题算法思路 【2023】

    使用说明 参加华为od机试,一定要注意不要完全背诵代码,需要理解之后模仿写出,通过率才会高. 华为 OD 清单查看地址:https://blog.csdn.net/hihell/category_12 ...

  5. 【华为OD机试真题 python】补种未成活胡杨 【2022 Q4 | 100分】

    前言 <华为OD笔试真题 python> 专栏含华为OD机试真题.华为面试题.牛客网华为专栏真题. 如果您正在准备华为的面试,或者华为od的机会,有任何想了解的可以私信我进行交流.我会尽可 ...

  6. 【华为OD机试真题 python】星际篮球争霸赛【2022 Q4 | 100分】

    前言 <华为OD笔试真题 python> 本专栏包含华为OD机试真题,会实时更新收纳网友反馈,为大家更新最新的华为德科OD机试试题,为大家提供学习和练手的题库,订阅本专栏后可私信进交流群哦 ...

  7. 【华为OD机试真题 python】用户调度问题 【2022 Q4 | 100分】

    前言 <华为OD笔试真题 python> 专栏含华为OD机试真题.华为面试题.牛客网华为专栏真题. 如果您正在准备华为的面试,或者华为od的机会,有任何想了解的可以私信我进行交流.我会尽可 ...

  8. 【华为OD机试真题 python】篮球比赛 【2022 Q4 | 100分】

    前言 <华为OD笔试真题 python> 专栏含华为OD机试真题.华为面试题.牛客网华为专栏真题. 如果您正在准备华为的面试,或者华为od的机会,有任何想了解的可以私信我进行交流.我会尽可 ...

  9. 【华为OD机试真题 python】相对开音节 【2022 Q4 | 100分】

    前言 <华为OD笔试真题 python> 专栏含华为OD机试真题.华为面试题.牛客网华为专栏真题. 如果您正在准备华为的面试,或者华为od的机会,有任何想了解的可以私信我进行交流.我会尽可 ...

最新文章

  1. hibernate02环境的搭建
  2. 测试设计中需要考虑的22种测试类型
  3. php++mpdf.mpdf,使用php第三方包mpdf将网页装换成pdf文件【2】
  4. C++ vector中的resize,reserve,size和capacity函数讲解
  5. lamp架构-访问控制-禁止php解析、屏蔽curl命令访问
  6. iOS-单例设计模式
  7. c# windows服务状态、启动和停止服务
  8. 【实践】短视频场景下信息流广告的挑战和技术实践.pdf(附下载链接)
  9. 送书 | Web前端性能优化
  10. Facebook分享动态内容
  11. QT 使用 qcustomplot 编译出错
  12. 瑞禧靶向光热染料ICG偶联-吲哚菁绿标记Labeled泛素/黑接骨木凝集素SNA / EBL
  13. 记录FinalShell退格键
  14. props的基本使用和特点
  15. WEB渗透之SQL 注入
  16. 阿里Java面试之-Java高级工程师
  17. 廖雪峰Python教程笔记
  18. 建模雕刻软件ZBrush,新手该如何学习,基础差该如何提升?
  19. 5折交叉验证_测试集训练集验证
  20. [Java基础]-- java char基本数据类型

热门文章

  1. 全国考研计算机成绩排名,计算机考研模拟考试全国排行
  2. MySQL_MySQL基础查询(DQL)
  3. Android 设置壁纸被拉伸(固定壁纸 )
  4. preall点云粗对齐作为matlab函数,pca和普氏分析法都有函数,主成分也有函数
  5. Volatile重排序规则的一些理解
  6. 软件自动更新功能的实现
  7. sql函数: 多级树状目录-根据父ID查询出所有的子ID
  8. win2012R2安装KB2919355 补丁的问题
  9. 学习HM微博项目第1天
  10. 必备软件——下载工具