java算法竞赛_Java在算法竞赛中的一些技巧

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

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

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

Java的快速输入

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

利用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++,可以参考我的第一篇博客。)

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

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

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

  • 3
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值