转载请注明出处(~ ̄▽ ̄)~

  谈到算法竞赛中使用Java,那么有一个绕不开的点就是如何快速地输入输出。通常来说,Scanner类固然可以帮助我们顺利地完成各种输入要求,而syso(System.out.print)也能够满足一般的输出要求,但是在内存以及时间的消耗上却不尽人意。同时,我发现国内博客似乎对Java在算法竞赛方面的技巧讨论得比较少,即使有也相对分散,不够深入。

  那么今天我就想分享一下自己在网上所看到的一些Java在算法竞赛方面的小技巧,希望对以Java作为算法竞赛主要语言的人有所帮助。

Java的快速输入

  算法竞赛的输入模式中,最难处理的莫过于“输入包括多行,读到文件末尾为止”这种输入要求,那今天就以这种输入要求为例。

  1. 利用StreamTokenizer
//从标准输入流输入
StreamTokenizer st = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));

比如经典的A+B问题,就可以这么去写

StreamTokenizer st = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
while(st.nextToken()!=StreamTokenizer.TT_EOF) {
int a,b;
a=(int)st.nval;
st.nextToken();
b=(int)st.nval;
pw.println(a+b);
}

如果处理的是字符串,则应该这么写

StreamTokenizer st = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
st.nextToken();
String str = st.sval;

即每次先使用st.nextToken(),再调用nval或sval。

优点

代码量少,便于记忆。

缺点

不能将12345这样的纯数字字符串通过sval读入,StreamTokenizer会将其识别为数字Token,只能通过nval读入,再利用String.valueOf()等方法转化为字符串。



2. 利用InputReader(非内建类)

//本人有少许改动
static class InputReader{
BufferedReader br;
StringTokenizer st; public InputReader(){
br = new BufferedReader(new InputStreamReader(System.in));
} String next() {
while(st==null||!st.hasMoreTokens()) {
try {
st = new StringTokenizer(br.readLine());
}catch(Exception e) {
break;
}
}
return st.nextToken();
} int nextInt() {
return Integer.parseInt(next());
} String nextLine(){
String str = "";
try {
str = br.readLine();
} catch (IOException e) {
e.printStackTrace();
}
return str;
} BigInteger nextBigInteger() {
BigInteger bi = null;
try {
bi = new BigInteger(br.readLine());
}catch(Exception e) { }
return bi;
} }

  InputReader应该是Java快读方面用得比较多的一个类了,它是由Petr大神设计的一个类,具有适用面广泛,相对好记的特点。(虽然其中使用到的StringTokenizer在官方文档中被评价道:"StringTokenizer is a legacy class that is retained for compatibility reasons although its use is discouraged in new code." 但是借用同学的话,这是宝贵的遗产,233)

  使用的话非常简单,他的API名称跟Scanner保持了一致,也能直接“顾名思义”。

那么如何处理多行输入呢,我是这么写的:

InputReader ir = new InputReader();
int a,b;
try {
while((a = ir.nextInt())!= Float.NaN && ( b = ir.nextInt())!= Float.NaN ) {
System.out.println(a+b);
}
}catch(Exception e) { }

优点

通用性强,易用性高

缺点

速度上也许是本文三种快速读中相对最慢的(相差不大),相比第一种方法相对要记多一些代码



3. 自定义读取和处理字节的函数

private byte[] inbuf = new byte[1024];
public int lenbuf = 0, ptrbuf = 0; private int readByte()
{
if(lenbuf == -1)throw new InputMismatchException();
if(ptrbuf >= lenbuf){
ptrbuf = 0;
try { lenbuf = System.in.read(inbuf); } catch (IOException e) { throw new InputMismatchException(); }
if(lenbuf <= 0)return -1;
}
return inbuf[ptrbuf++];
} private boolean isSpaceChar(int c) { return !(c >= 33 && c <= 126); }
private int skip() { int b; while((b = readByte()) != -1 && isSpaceChar(b)); return b; } private double nd() { return Double.parseDouble(ns()); }
private char nc() { return (char)skip(); } private String ns()
{
int b = skip();
StringBuilder sb = new StringBuilder();
while(!(isSpaceChar(b))){ // when nextLine, (isSpaceChar(b) && b != ' ')
sb.appendCodePoint(b);
b = readByte();
}
return sb.toString();
} private char[] ns(int n)
{
char[] buf = new char[n];
int b = skip(), p = 0;
while(p < n && !(isSpaceChar(b))){
buf[p++] = (char)b;
b = readByte();
}
return n == p ? buf : Arrays.copyOf(buf, p);
} private char[][] nm(int n, int m)
{
char[][] map = new char[n][];
for(int i = 0;i < n;i++)map[i] = ns(m);
return map;
} private int[] na(int n)
{
int[] a = new int[n];
for(int i = 0;i < n;i++)a[i] = ni();
return a;
} private int ni()
{
int num = 0, b;
boolean minus = false;
while((b = readByte()) != -1 && !((b >= '0' && b <= '9') || b == '-'));
if(b == '-'){
minus = true;
b = readByte();
} while(true){
if(b >= '0' && b <= '9'){
num = num * 10 + (b - '0');
}else{
return minus ? -num : num;
}
b = readByte();
}
} private long nl()
{
long num = 0;
int b;
boolean minus = false;
while((b = readByte()) != -1 && !((b >= '0' && b <= '9') || b == '-'));
if(b == '-'){
minus = true;
b = readByte();
} while(true){
if(b >= '0' && b <= '9'){
num = num * 10 + (b - '0');
}else{
return minus ? -num : num;
}
b = readByte();
}
}

  上述相关函数由uwi大神设计,orz,自己亲自处理字节,速度当然是杠杠的。函数名符合一贯竞赛er的抽象惯例 在处理多行A+B问题时,我的写法和方法二类似,即:

int a,b;
try {
while((a = ni())!= Float.NaN && ( b = ni())!= Float.NaN ) {
System.out.println(a+b);
}
}catch(Exception e) { }

优点

速度快 够装逼

缺点

emmm,应该不太可能现场背下来,比较适合网络赛和平时练习之类的

Java的快速输出

  相比于快速输入的各种神仙写法,快速输出可以说是非常友好了,不仅便于记忆,而且能在很短的时间内写出来。

PrintWriter pw = new PrintWriter(System.out);
pw.println(//和System.out.println用法一样);
pw.close();//这句话在最后必须有,不然不会将结果输出

  由于PrintWriter自带Buffer,所以不需要再对System.out再套(娃)其他的缓冲输出流。简直不要太好用有木有?



  快速输入输出这套组合拳,如果你拿来跟之前的Scanner和System.out输入输出相比,你就会发现时间和空间耗费大大减少了,以前A不过的题再也不是问题了。并不

Java加栈

  还有一个问题,爆栈了怎么办?特别是遇上数据量大的题目,爆栈简直不要太容易,当然我们可以通过改递归为遍历等方法来规避这个问题,但是有没有什么奇技淫巧呢?当然有:

//还是以A+B问题为例
public class Main implements Runnable {
public static void main(String[] args) {
//whatever可替换为任何内容
new Thread(null, new Main(), "whatever", 1<<26).start();
} public void run() {
Scanner cin = new Scanner(System.in);
int a, b;
while (cin.hasNext()){
a = cin.nextInt(); b = cin.nextInt();
System.out.println(a + b);
}
}
}

  没错,方法就是开一个线程,通过为线程申请空间从而解决这个问题。但是注意,一是start方法执行后也许不会立即执行run方法,二是申请的空间大小是平台相关的,不保证一定有效。



  后记:虽然本文介绍了快速读写和加栈的方法,但是归根结底,算法竞赛重点还是希望大家写出高效的算法,这些只是一些辅助的内容,时间复杂度不对,单凭这些“优化”仍是无法解决问题的。(此外,遇到腹黑的出题人,即便你的时间复杂度没问题,也用了快速输入输出,仍然是无法AC的,这就要谈到Java在算法竞赛圈中的卑微地位了(划掉),所以最好的优化就是使用C++(狗头)。而关于如何转向C++,可以参考我的第一篇博客。)



  要是觉得不错,就点个“推荐”吧 φ(゜▽゜*)♪



如有错误,还望不吝指出。

转载请注明出处(~ ̄▽ ̄)~

Java在算法竞赛中的一些技巧的更多相关文章

  1. 算法竞赛中的常用JAVA API:PriorityQueue(优先队列)(转载)

    算法竞赛中的常用JAVA API:PriorityQueue(优先队列) PriorityQueue 翻译过来就是优先队列,本质是一个堆, 默认情况下堆顶每次都保留最小值,每插入一个元素,仍动态维护堆 ...

  2. 算法竞赛中的常用JAVA API :HashSet 和 TreeSet(转载)

    算法竞赛中的常用JAVA API :HashSet 和 TreeSet set set容器的特点是不包含重复元素,也就是说自动去重. HashSet HashSet基于哈希表实现,无序. add(E ...

  3. 算法竞赛中的常用JAVA API :HashMap 和 TreeMap(转载)

    算法竞赛中的常用JAVA API :HashMap 和 TreeMap 摘要 本文主要介绍Map接口下的HashMap和TreeMap. HashMap HashMap是基于哈希表的 Map 接口的实 ...

  4. 8.算法竞赛中的常用JAVA API :Calendar日期类

    8.算法竞赛中的常用JAVA API :Calendar日期类 摘要 在蓝桥杯中有关于日期计算的问题,正好java中的Date类和Calendar类提供了对日期处理的一些方法.Date类大部分方法已经 ...

  5. 7.算法竞赛中的常用JAVA API :String 、StringBuilder、StringBuffer常用方法和区别(转载)

    7.算法竞赛中的常用JAVA API :String .StringBuilder.StringBuffer常用方法和区别 摘要 本文将介绍String.StringBuilder类的常用方法. 在j ...

  6. 6.算法竞赛中的常用JAVA API :Math类(转载)

    6.算法竞赛中的常用JAVA API :Math类 求最值 最小值 Math.min(int a, int b) Math.min(float a, float b) Math.min(double ...

  7. 算法竞赛中的常用JAVA API :大数类(转载)

    5.算法竞赛中的常用JAVA API :大数类 摘要 java中的基础数据类型能存储的最大的二进制数是 2 ^ 63 - 1 对应的十进制数是9223372036854775807(long类型的最大 ...

  8. Aho-Corasick automaton(AC自动机)解析及其在算法竞赛中的典型应用举例

    摘要: 本文主要讲述了AC自动机的基本思想和实现原理,如何构造AC自动机,着重讲解AC自动机在算法竞赛中的一些典型应用. 什么是AC自动机? 如何构造一个AC自动机? AC自动机在算法竞赛中的典型应用 ...

  9. STL函数 lower_bound 和 upper_bound 在算法竞赛中的用法

    以前比较排斥这两个函数,遇到需要二分的情景都是手写 \(while(left<=right)\). 这次决定洗心革面记录一下这两个函数的在算法竞赛中的用法,毕竟一般不会导致TLE. 其实百度百科 ...

  10. Java在算法题中的输入问题

    Java在算法题中的输入问题 在写算法题的时候,经常因为数据的输入问题而导致卡壳,其中最常见的就是数据输入无法结束. 1.给定范围,确定输入几个数据 直接使用普通的Scanner输入数据范围,然后使用 ...

随机推荐

  1. Sqlmap注入dvwa平台low级别

    工具介绍:sqlmap是一款开源的软件 SQL注入攻击是黑客对数据库进行攻击的常用手段之一.随着B/S模式应用开发的发展,使用这种模式编写应用程序的程序员也越来越多.但是由于程序员的水平及经验也参差不 ...

  2. IDA 特征码生成和搜索脚本

    最近比较忙,就少写两句,直接附上源代码,其中的细节点就不再赘述,如有疑问,请留言. 一共就是实现了两个函数,一个用于搜索特征码 (SearchPattern),一个用于生成特征码 (GenerateF ...

  3. 由 Base64 展开的知识探讨

    我们是袋鼠云数栈 UED 团队,致力于打造优秀的一站式数据中台产品.我们始终保持工匠精神,探索前端道路,为社区积累并传播经验价值.. 本文作者:霜序(掘金) 前言 在我们的业务应用中越来越多的应用到编 ...

  4. mybatis:Could not set parameters for mapping: ParameterMapping

    报错: 展开查看 org.apache.ibatis.exceptions.PersistenceException: ### Error querying database. Cause: org. ...

  5. abp(net core)+easyui+efcore实现仓储管理系统——组织管理升级之下(六十二)

    Abp(net core)+easyui+efcore实现仓储管理系统目录 abp(net core)+easyui+efcore实现仓储管理系统--ABP总体介绍(一) abp(net core)+ ...

  6. 2020-11-01:rust中带move闭包和不带move闭包有什么区别?

    福哥答案2020-11-01: 1.是否是同一个变量:带move闭包,函数外和函数内的同名变量不是同一个变量.不带move闭包,函数外和函数内的同名变量是同一个变量.2.执行完闭包后:带move闭包, ...

  7. 2022-05-22:给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。 百度百科中最近公共祖先的定义为:“对于有根树 T 的两个节点 p、q,最近公共祖先表示为一个节点 x,满足 x 是 p

    2022-05-22:给定一个二叉树, 找到该树中两个指定节点的最近公共祖先. 百度百科中最近公共祖先的定义为:"对于有根树 T 的两个节点 p.q,最近公共祖先表示为一个节点 x,满足 x ...

  8. Selenium - 浏览器操作

    Selenium - 浏览器操作 获取浏览器信息 from selenium import webdriver driver = webdriver.Chrome() driver.get(" ...

  9. json函数

    Python与JSON(load.loads.dump.dumps)   1.Python中加载JSON 使用loads(string):作用将string类型转为dict字典或dict链表 # 加载 ...

  10. 回溯理论基础及leetcode例题

    学习参考 回溯 与递归相辅相成;回溯是递归的副产品,只要有递归就会有回溯. 回溯函数也就是递归函数,指的都是一个函数. 回溯搜索法 纯暴力搜索 解决的问题 组合问题:N个数里面按一定规则找出k个数的集 ...