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++,可以参考我的第一篇博客。)
要是觉得不错,就点个“推荐”吧 φ(゜▽゜*)♪
如有错误,还望不吝指出。
转载请注明出处(~ ̄▽ ̄)~
Java在算法竞赛中的一些技巧的更多相关文章
- 算法竞赛中的常用JAVA API:PriorityQueue(优先队列)(转载)
算法竞赛中的常用JAVA API:PriorityQueue(优先队列) PriorityQueue 翻译过来就是优先队列,本质是一个堆, 默认情况下堆顶每次都保留最小值,每插入一个元素,仍动态维护堆 ...
- 算法竞赛中的常用JAVA API :HashSet 和 TreeSet(转载)
算法竞赛中的常用JAVA API :HashSet 和 TreeSet set set容器的特点是不包含重复元素,也就是说自动去重. HashSet HashSet基于哈希表实现,无序. add(E ...
- 算法竞赛中的常用JAVA API :HashMap 和 TreeMap(转载)
算法竞赛中的常用JAVA API :HashMap 和 TreeMap 摘要 本文主要介绍Map接口下的HashMap和TreeMap. HashMap HashMap是基于哈希表的 Map 接口的实 ...
- 8.算法竞赛中的常用JAVA API :Calendar日期类
8.算法竞赛中的常用JAVA API :Calendar日期类 摘要 在蓝桥杯中有关于日期计算的问题,正好java中的Date类和Calendar类提供了对日期处理的一些方法.Date类大部分方法已经 ...
- 7.算法竞赛中的常用JAVA API :String 、StringBuilder、StringBuffer常用方法和区别(转载)
7.算法竞赛中的常用JAVA API :String .StringBuilder.StringBuffer常用方法和区别 摘要 本文将介绍String.StringBuilder类的常用方法. 在j ...
- 6.算法竞赛中的常用JAVA API :Math类(转载)
6.算法竞赛中的常用JAVA API :Math类 求最值 最小值 Math.min(int a, int b) Math.min(float a, float b) Math.min(double ...
- 算法竞赛中的常用JAVA API :大数类(转载)
5.算法竞赛中的常用JAVA API :大数类 摘要 java中的基础数据类型能存储的最大的二进制数是 2 ^ 63 - 1 对应的十进制数是9223372036854775807(long类型的最大 ...
- Aho-Corasick automaton(AC自动机)解析及其在算法竞赛中的典型应用举例
摘要: 本文主要讲述了AC自动机的基本思想和实现原理,如何构造AC自动机,着重讲解AC自动机在算法竞赛中的一些典型应用. 什么是AC自动机? 如何构造一个AC自动机? AC自动机在算法竞赛中的典型应用 ...
- STL函数 lower_bound 和 upper_bound 在算法竞赛中的用法
以前比较排斥这两个函数,遇到需要二分的情景都是手写 \(while(left<=right)\). 这次决定洗心革面记录一下这两个函数的在算法竞赛中的用法,毕竟一般不会导致TLE. 其实百度百科 ...
- Java在算法题中的输入问题
Java在算法题中的输入问题 在写算法题的时候,经常因为数据的输入问题而导致卡壳,其中最常见的就是数据输入无法结束. 1.给定范围,确定输入几个数据 直接使用普通的Scanner输入数据范围,然后使用 ...
随机推荐
- Github 添加贪吃蛇动画
前言 我们都知道,对于Github来说,当你选择你的账户时,可以看到自己的提交记录. 于是就有大神动脑筋了,这些commit记录都是一些豆,如果弄一条蛇来,不就可以搞个贪吃蛇了吗? 有道理有道理,本文 ...
- Nvidia GPU池化-远程GPU
1 背景 Nvidia GPU得益于在深度学习领域强大的计算能力,使其在数据中心常年处于绝对的统治地位.尽管借助GPU虚拟化实现多任务混布,提高了GPU的利用率,缓解了长尾效应,但是GPU利用率的绝对 ...
- .NET周报 【4月第4期 2023-04-23】
国内文章 2023成都.NET线下技术沙龙圆满结束 https://www.cnblogs.com/edisonchou/p/2023_chengdu_dotnet_club_activity_rev ...
- react中受控组件与非受控组件--
非受控组件:随用随取 1 render() { 2 return ( 3 <div> 4 <h1>非受控组件</h1> 5 <form action=&quo ...
- day07-优惠券秒杀03
功能03-优惠券秒杀03 4.功能03-优惠券秒杀 4.6Redisson的分布式锁 Redis分布式锁-Redisson+RLock可重入锁实现篇 4.6.1基于setnx实现的分布式锁问题 我们在 ...
- 《C和指针》第一章
1 第一章 C标准库中几个常用的IO函数 int puts(void *str): 从str中提取字符直到遇到第一个'\0'为止,将这些字符串加上'\n'后发送给stdout. int main(vo ...
- XMake学习笔记(1):Windows(MSYS2)下MinGW-w64环境搭建和XMake安装
以前写的C++基本都是C with STL,大多是面向过程的算法题,或者比较小的项目,然后经常报各种编译错误(对编译原理不熟),经常把人搞到崩溃,搞不懂构建.链接之类的东西. 现在开始记录一下XMak ...
- \n 和 std::endl 的区别
std::cout << std::endl; 等价于 std::cout << '\n' << std::flush; 除了写入换行符,std::endl 还会刷 ...
- Pytorch数据操作
1.Pytorch中tensor的生成与访问 可以使用arange()创建一个张量:如,torch.arange(12)创建0开始的前12个整数: 除非特殊指定,否则新的张量将存放在内存中,并采用CP ...
- 2023-05-17:一个正整数如果能被 a 或 b 整除,那么它是神奇的。 给定三个整数 n , a , b ,返回第 n 个神奇的数字。 因为答案可能很大,所以返回答案 对 10^9 + 7 取模
2023-05-17:一个正整数如果能被 a 或 b 整除,那么它是神奇的. 给定三个整数 n , a , b ,返回第 n 个神奇的数字. 因为答案可能很大,所以返回答案 对 10^9 + 7 取模 ...