ACM中使用JAVA v2.1
2014-09-28 15:14
375 查看
ACM中使用JAVA v2.1
严明超
(Blog:mingchaoyan.blogbus.com
Email:mingchaoyan@gmail.com)
文前声明:本文只谈java用于acm,且不推荐大家把java作为自己的第一语言玩acm;
为防止大家过多的把本应学习算法的宝贵时间浪费在学习语言上,同时也为实现lcy一队一java的要求;我根据自己用java的经验,总结成文,希望可以抛砖引玉,帮助大家快速学会用java ac;
Java对熟悉c/c++的选手来说应该是似曾相识的,因为它本身就是用相似c/c++结构设计的。一些安装jdk,配置环境变量,以Main做类名等等这些我就不多说了。IDE推荐使用Eclipse,你可以在这里下载。Eclipse是个平台,所以你也可以装个插件来跑c/c++,插件怎么装可以看看这里(正规比赛一般都有Eclipse,没vs,因为vs是windows下的,正规比赛肯定不会用windows,为什么,你懂的);
说了点无关的话,进入正题,在这里你将看到以下内容:1,输入输入,2.,大数/高精度,3,排序,4“STL”,5,其他
输入首推Scanner,输出首推System.out.pritnln()/System.out.print();
这个学过java的一般都知道怎么用,先以标准输入为参数new 一个Scanner,如果要读入一个int,就用nextInt(),double 就用nextDouble,其他类型依次类推,另外可以用hasNextInt来判断是否还有下一个。至于读入以字符串用next(),读入一行用nextLine();
附上A+B代码
[c-sharp] view
plaincopy
import java.io.*;
import java.util.*;
public class Main {
public static void main(String args[]) {
Scanner cin = new Scanner(System.in);
int a, b;
while (cin.hasNextInt()) {
a = cin.nextInt();
b = cin.nextInt();
System.out.println(a + b);
}
}
}
1.2 StreamTokenizer和PrintWriter
但是用Scanner输入就像cin那样比较慢,当数据量一大会超时的,我记得有一个题目(忘了具体哪个题目),很直白的bfs就会超时,不排除你有很强的剪枝(虽然这样的情况的确很少,一般Scanner用用足够了),此时不得不用StreamTokenizer 和 PrintWriter ,请看看注释(这里格式比较乱,推荐大家把他copy到Eclipse下 ctrl+Shift + f (格式化代码)了再读):
[c-sharp] view
plaincopy
import java.io.*;
public class Main {
public static void main(String[] args) throws IOException
// in.nextTokenizer可能抛出这个异常
{
StreamTokenizer in = new StreamTokenizer(new BufferedReader(
new InputStreamReader(System.in)));
// 这句是io流包装来包装去,记住就好;
PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
int a, b;
while (in.nextToken() != StreamTokenizer.TT_EOF)
// 用in.nextToken()读下一个标记,StreamTokenizer.TT_EOF这个是个参数,就是EOF
{
a = (int) in.nval;
// 读进来的是double型的,所以要转成int
in.nextToken();
// 读入b值(a的值是在while循环中读入的)
b = (int) in.nval;
out.println(a + b);
}
out.flush();
// 刷新缓冲区,必须的,不然a+b会留在缓冲区
}
}
一般也用不到PrintWriter ,除非像这个输出结果数据量很大,那用PrintWriter就会快很多了;
另外对于输入,jdk 5.0以后可以用System.out.printf()这个格式化输出了,你可以把它和printf联系起来,类似的,比如输出小数点后3位System.out.printlnf("%.f%n",a);注意引号中的%n,这个是表示与平台无关的换行,如果一定要用那个我们熟悉的/n,那就请在加一个/r,用/n/r,不然PE,搞不好还WA。。。这个问题是很著名的不同平台回车和换行的尴尬,我就不多解释了;
还有一个问题,测试文件的输入输出。就像用C++时,只要简单的在main下添加两个freopen,java中也有类似的方法;
[c-sharp] view
plaincopy
FileInputStreamfin=newFileInputStream("/home/JCrzer/workspace/ACM/in.txt");
PrintStreamfout=newPrintStream("/home/JCrazer/workspace/ACM/out.txt");
System.setIn(fin);
System.setOut(fout);
Windows要不"/"改成"//",或者索性改成"/",你懂的。。。。
用java写acm应该就是冲着这个来的吧^_^,java中有两个类BigDecimal 和 BigInteger。看名字就知道他们的区别了
以下是hdoj1002代码,很直白的,不多解释;
来看hdoj1753
[c-sharp] view
plaincopy
import java.math.BigDecimal;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
BigDecimal a, b;
while (in.hasNext()) {
a = in.nextBigDecimal();
b = in.nextBigDecimal();
System.out.println(a.add(b).stripTrailingZeros().toPlainString());
}
}
}
因为高精度加法结果的精度是取两个加数中较大的那个,所以可能会出现后置0,这时就要通过stripTrailingZeros()把后置0去掉,又因为,BigDecimal的toString()方法可能返回的是科学技术法表示的,所以要用toPlainString()转成普遍计数法;
关于大数高精度其实还有很多问题,比如初始化构造,精度,比如舍入等等,这些零零碎碎的东西太多了,都举个例子写下会累死我的,我也不想把api中的内容copy来贴在这里充字数,所以建议平时多查查api(在这里查,中文版),还有有什么需要留言吧~;
大数可能之前大家也敲过,排序可能是大家陌生的,因为java写排序和c/c++差别有点大;
最简单的排序Arrays.sort(a),默认是按数组a从小到大排序的,如果是String就按字典序,而且对指定范围的排序也已经有重载版本,%20int,%20int%29]sort(Object[] a, int fromIndex, int toIndex) 其中和c++中类似,此方法排序时,范围包括fromIndex,,不包括toIndex。但只是这种单一的排序方式显然不能满足我们变幻莫测排序要求。排序有两种方法:
这个比较常用,就是在设计Node这个类时(java中没有struct关键词,都要用class代替),实现Comparable这个接口,重写其compareTo这个方法,在这个方法中规定你要的排序顺序。如此在接下来的对Node实例数组排序时,就是按照你定义的排序顺序来排的;比如:
[c-sharp] view
plaincopy
import java.util.Arrays;
class Node implements Comparable {
int a;
Node(int a) {
this.a = a;
}
@Override
public int compareTo(Object arg0) {
int a = ((Node) arg0).a;
if (this.a < a)
return -1;
else if (this.a > a)
return 1;
return 0;
//熟练了就可以犀利的写成 return this.a-a; 但下文都用以上“规范清晰”的写法
}
@Override
public String toString() {
return "" + a;
}
}
public class Main {
public static void main(String[] args) {
Node[] node = new Node[4];
node[0] = new Node(9);
node[1] = new Node(8);
node[2] = new Node(100);
node[3] = new Node(0);
Arrays.sort(node);
System.out.println(Arrays.toString(node));
}
}
运行结果:
[0, 8, 9, 100]
说明3点:
1.在Node类中的toString是只是我为了打印运行结果而重写的,与排序无关;
2.Node有多个属性时,当然可以根据需要二级排序,三级排序等等;
3.这样设计Node之后只能按照你定义的排序了,如果对一个Node要用不同的标准排序,请看下面:
使用Comparator就不像上面那样比较死板了,你可以设计Cmp1,Cmp2来满足你不同标准排序的要求
比如下面是我随便举的例子,标准1:对人员先按照名字升序,如果名字相同按照年龄升序 ,标准2:先对年龄升序,如果年龄相同就按姓名升序
[c-sharp] view
plaincopy
import java.util.Arrays;
import java.util.Comparator;
class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return this.name;
}
public int getAge() {
return this.age;
}
@Override
public String toString() {
// TODO Auto-generated method stub
return "" + this.name + " " + this.age;
}
}
class Cmp1 implements Comparator {
@Override
public int compare(Object arg0, Object arg1) {
Person a = (Person) arg0;
Person b = (Person) arg1;
if (a.getName().compareTo(b.getName()) != 0)
return a.getName().compareTo(b.getName());
else {
if (a.getAge() < b.getAge())
return -1;
else if (a.getAge() > b.getAge())
return 1;
else
return 0;
}
}
}
class Cmp2 implements Comparator {
@Override
public int compare(Object arg0, Object arg1) {
Person a = (Person) arg0;
Person b = (Person) arg1;
if (a.getAge() < b.getAge())
return -1;
else if (a.getAge() > b.getAge())
return 1;
else
return a.getName().compareTo(b.getName());
}
}
public class Main {
public static void main(String[] args) {
Person[] p = new Person[4];
p[0] = new Person("ZZZ", 19);
p[1] = new Person("AAA", 109);
p[2] = new Person("AAA", 19);
p[3] = new Person("YYY", 100);
Arrays.sort(p, new Cmp1());
System.out.println("Cmp1");
System.out.println(Arrays.toString(p));
Arrays.sort(p, new Cmp2());
System.out.println("Cmp2");
System.out.println(Arrays.toString(p));
}
}
运行结果;
Cmp1
[AAA 19, AAA 109, YYY 100, ZZZ 19]
Cmp2
[AAA 19, ZZZ 19, YYY 100, AAA 109]
这种方法在CMP这个类设计好后,要排序时直接在Arrays.sort()中new一个CMP实例就好了,是不是很像c++中sort,或者qsort。由于这个类只在排序时使用,所以也可以设计成匿名内部类(只对Cmp1举例),代码如下;
[c-sharp] view
plaincopy
import java.util.Arrays;
import java.util.Comparator;
class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return this.name;
}
public int getAge() {
return this.age;
}
@Override
public String toString() {
return "" + this.name + " " + this.age;
}
}
public class Main {
public static void main(String[] args) {
Person[] p = new Person[4];
p[0] = new Person("ZZZ", 19);
p[1] = new Person("AAA", 109);
p[2] = new Person("AAA", 19);
p[3] = new Person("YYY", 100);
Arrays.sort(p, new Comparator<Object>() {
@Override
public int compare(Object arg0, Object arg1) {
Person a = (Person) arg0;
Person b = (Person) arg1;
if (a.getName().compareTo(b.getName()) != 0)
return a.getName().compareTo(b.getName());
else {
if (a.getAge() < b.getAge())
return -1;
else if (a.getAge() > b.getAge())
return 1;
else
return 0;
}
}
});
System.out.println(Arrays.toString(p));
}
}
匿名内部类是没有名字的内部类,你可以理解我把CMP这个类设计在了Arrays.sort()这个方法的参数中。你若java中这种机制不是很理解,就用前面那种吧。
之所以对STL加引号,是因为JAVA中似乎不叫标准模板库,不过这个只是称呼,你知道我是在说这个就好了。很多前辈都提出了“STL伤代码能力论”我只是把它对应C++的那些列出来,用不用你看着办吧:
关于String我只想说这个是常量,创建了以后永远不会变的,想要变就用StringBuffer;这个有很多意想不到的方法,比如split方法可以根据给定的字符拆分字符串,方便吧,自己处理会麻烦死的还容易出错,再比如这两个都支持正则,正则的强大不需要我解释,另外还有很多,查api去吧~~~
就是表,可以用LinkedList类(可以理解链表实现所以增删比较快),ArrayList类(理解为数组实现访问比较快),当然还有大家很熟悉的Vector类;
创建 List ll = new LinkedList();(你也可以LinkedList ll = new LinkedList();但推荐使用前者,因为在创建对象是,使用接口的引用指向对象是一个好习惯~)
方法: add(e), get(int index), clear(),isEmpty(),size()。。。(方法太多了,C++中实现的方法JAVA中差不多都有增无减地实现了,真的不想一一列出来,还是那句话,查api吧,真不行就留言)
JAVA集合框架中很多方法都是通用的,下面的有些方法就不再列举了;
映射,有Hashtable类(Hashtable的同步版本),LinkedHashMap类(按元素插入顺序排序),HashMap类(比较常用),TreeMap类(按元素的自然顺序排序,这个的存储方式是传说中的红黑树~)可以实现;使用put方法添加元素,get(key)方法获得对应key的值,containsKey(key)方法判断是否已经存在此映射;
集合,有HashSet类(没有排序),LinkedHashSet类(按插入顺序排序),TreeSet类(按自然序排序)可以实现;
队列,用实现List的那些类实现好了,offer方法入队,poll方法取队头并扔掉头(若不想扔掉用peek方法);
优先队列,注意这个是类,所以创建时一定要PriorityQueue qp = new PriotityQueue();方法和Queue类似;
栈,注意这个也是类,push压入栈,poll取栈顶并扔掉(同样peek取顶不扔掉);
最后举一个例子吧HDOJ1873(这个例子可以看到这些“玩具”的数组怎么创建)
[c-sharp] view
plaincopy
import java.io.*;
import java.util.PriorityQueue;
public class Main {
public static class Patient implements Comparable {
int id, priority;
public Patient(int id, int priority) {
this.id = id;
this.priority = priority;
}
@Override
public int compareTo(Object arg0) {
// 设置病人的优先级
int priority = ((Patient) arg0).priority;
int id = ((Patient) arg0).id;
if (this.priority > priority)
return -1;
else if (this.priority < priority)
return 1;
else
return this.id - id;
}
}
public static void main(String[] args) throws IOException {
StreamTokenizer in = new StreamTokenizer(new BufferedReader(
new InputStreamReader(System.in)));
while (in.nextToken() != StreamTokenizer.TT_EOF) {
int n = (int) in.nval;
int id = 1;
PriorityQueue<Patient>[] pq = new PriorityQueue[3];
// PriorityQueue<Patient> 泛型,new PriorityQueue[3]后面就不要加<Patient>了
for (int i = 0; i < 3; ++i) {
pq[i] = new PriorityQueue<Patient>();
}
for (int i = 0; i < n; ++i) {
in.nextToken();
String flag = in.sval;
if (flag.equals("OUT")) {
// String的等值比较,不要用==呀!!!!!!!!
in.nextToken();
int doctor = (int) in.nval;
if (pq[doctor - 1].isEmpty())
// 是否为空
System.out.println("EMPTY");
else
System.out.println(pq[doctor - 1].poll().id);
// 取队头并扔掉
} else {
in.nextToken();
int doctor = (int) in.nval;
in.nextToken();
int priority = (int) in.nval;
pq[doctor - 1].offer(new Patient(id++, priority));
}
}
}
}
}
5 其他
想不好再写什么,看大家需要吧~
关于传值问题:
Java中对象(强调8种基本数据类型除外)一定是引用传递的,所以当你不需要引用传递时,就拷贝一份副本传进去,当然拷贝可以用clone(),具体写法看以下例子:
本文给出一些用java做acm的基本用法,一般不是“纯java选手”,学会上面这些应付应付比赛中“突如其来”的大数高精度之类java有明显优势的题目足够了。但是不要迷恋java,java只是一种语言,语言是规则,算法是思想;
最后再次指出本文只谈java用于acm,且不推荐大家把java作为自己的第一语言玩acm,欢迎拍砖~~
严明超
(Blog:mingchaoyan.blogbus.com
Email:mingchaoyan@gmail.com)
0.前 言
文前声明:本文只谈java用于acm,且不推荐大家把java作为自己的第一语言玩acm;为防止大家过多的把本应学习算法的宝贵时间浪费在学习语言上,同时也为实现lcy一队一java的要求;我根据自己用java的经验,总结成文,希望可以抛砖引玉,帮助大家快速学会用java ac;
Java对熟悉c/c++的选手来说应该是似曾相识的,因为它本身就是用相似c/c++结构设计的。一些安装jdk,配置环境变量,以Main做类名等等这些我就不多说了。IDE推荐使用Eclipse,你可以在这里下载。Eclipse是个平台,所以你也可以装个插件来跑c/c++,插件怎么装可以看看这里(正规比赛一般都有Eclipse,没vs,因为vs是windows下的,正规比赛肯定不会用windows,为什么,你懂的);
说了点无关的话,进入正题,在这里你将看到以下内容:1,输入输入,2.,大数/高精度,3,排序,4“STL”,5,其他
1. 输入输出
1.1 Scanner
输入首推Scanner,输出首推System.out.pritnln()/System.out.print();这个学过java的一般都知道怎么用,先以标准输入为参数new 一个Scanner,如果要读入一个int,就用nextInt(),double 就用nextDouble,其他类型依次类推,另外可以用hasNextInt来判断是否还有下一个。至于读入以字符串用next(),读入一行用nextLine();
附上A+B代码
[c-sharp] view
plaincopy
import java.io.*;
import java.util.*;
public class Main {
public static void main(String args[]) {
Scanner cin = new Scanner(System.in);
int a, b;
while (cin.hasNextInt()) {
a = cin.nextInt();
b = cin.nextInt();
System.out.println(a + b);
}
}
}
1.2 StreamTokenizer和PrintWriter
但是用Scanner输入就像cin那样比较慢,当数据量一大会超时的,我记得有一个题目(忘了具体哪个题目),很直白的bfs就会超时,不排除你有很强的剪枝(虽然这样的情况的确很少,一般Scanner用用足够了),此时不得不用StreamTokenizer 和 PrintWriter ,请看看注释(这里格式比较乱,推荐大家把他copy到Eclipse下 ctrl+Shift + f (格式化代码)了再读):
[c-sharp] view
plaincopy
import java.io.*;
public class Main {
public static void main(String[] args) throws IOException
// in.nextTokenizer可能抛出这个异常
{
StreamTokenizer in = new StreamTokenizer(new BufferedReader(
new InputStreamReader(System.in)));
// 这句是io流包装来包装去,记住就好;
PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
int a, b;
while (in.nextToken() != StreamTokenizer.TT_EOF)
// 用in.nextToken()读下一个标记,StreamTokenizer.TT_EOF这个是个参数,就是EOF
{
a = (int) in.nval;
// 读进来的是double型的,所以要转成int
in.nextToken();
// 读入b值(a的值是在while循环中读入的)
b = (int) in.nval;
out.println(a + b);
}
out.flush();
// 刷新缓冲区,必须的,不然a+b会留在缓冲区
}
}
一般也用不到PrintWriter ,除非像这个输出结果数据量很大,那用PrintWriter就会快很多了;
另外对于输入,jdk 5.0以后可以用System.out.printf()这个格式化输出了,你可以把它和printf联系起来,类似的,比如输出小数点后3位System.out.printlnf("%.f%n",a);注意引号中的%n,这个是表示与平台无关的换行,如果一定要用那个我们熟悉的/n,那就请在加一个/r,用/n/r,不然PE,搞不好还WA。。。这个问题是很著名的不同平台回车和换行的尴尬,我就不多解释了;
1.3 文件
还有一个问题,测试文件的输入输出。就像用C++时,只要简单的在main下添加两个freopen,java中也有类似的方法;[c-sharp] view
plaincopy
FileInputStreamfin=newFileInputStream("/home/JCrzer/workspace/ACM/in.txt");
PrintStreamfout=newPrintStream("/home/JCrazer/workspace/ACM/out.txt");
System.setIn(fin);
System.setOut(fout);
Windows要不"/"改成"//",或者索性改成"/",你懂的。。。。
2 大数/高精度
用java写acm应该就是冲着这个来的吧^_^,java中有两个类BigDecimal 和 BigInteger。看名字就知道他们的区别了
2.1 大数
以下是hdoj1002代码,很直白的,不多解释;
[c-sharp] view
plaincopy
import java.math.*;
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int t = in.nextInt();
for (int i = 1; i <= t; ++i) {
if (i != 1)
System.out.println();
BigInteger a = in.nextBigInteger();
BigInteger b = in.nextBigInteger();
System.out.println("Case " + i + ":");
System.out.println(a + " + " + b + " = " + a.add(b));
}
}
}
2.2 高精度
来看hdoj1753[c-sharp] view
plaincopy
import java.math.BigDecimal;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
BigDecimal a, b;
while (in.hasNext()) {
a = in.nextBigDecimal();
b = in.nextBigDecimal();
System.out.println(a.add(b).stripTrailingZeros().toPlainString());
}
}
}
因为高精度加法结果的精度是取两个加数中较大的那个,所以可能会出现后置0,这时就要通过stripTrailingZeros()把后置0去掉,又因为,BigDecimal的toString()方法可能返回的是科学技术法表示的,所以要用toPlainString()转成普遍计数法;
关于大数高精度其实还有很多问题,比如初始化构造,精度,比如舍入等等,这些零零碎碎的东西太多了,都举个例子写下会累死我的,我也不想把api中的内容copy来贴在这里充字数,所以建议平时多查查api(在这里查,中文版),还有有什么需要留言吧~;
3排序
大数可能之前大家也敲过,排序可能是大家陌生的,因为java写排序和c/c++差别有点大;最简单的排序Arrays.sort(a),默认是按数组a从小到大排序的,如果是String就按字典序,而且对指定范围的排序也已经有重载版本,%20int,%20int%29]sort(Object[] a, int fromIndex, int toIndex) 其中和c++中类似,此方法排序时,范围包括fromIndex,,不包括toIndex。但只是这种单一的排序方式显然不能满足我们变幻莫测排序要求。排序有两种方法:
3.1 实现Comparable接口
这个比较常用,就是在设计Node这个类时(java中没有struct关键词,都要用class代替),实现Comparable这个接口,重写其compareTo这个方法,在这个方法中规定你要的排序顺序。如此在接下来的对Node实例数组排序时,就是按照你定义的排序顺序来排的;比如:[c-sharp] view
plaincopy
import java.util.Arrays;
class Node implements Comparable {
int a;
Node(int a) {
this.a = a;
}
@Override
public int compareTo(Object arg0) {
int a = ((Node) arg0).a;
if (this.a < a)
return -1;
else if (this.a > a)
return 1;
return 0;
//熟练了就可以犀利的写成 return this.a-a; 但下文都用以上“规范清晰”的写法
}
@Override
public String toString() {
return "" + a;
}
}
public class Main {
public static void main(String[] args) {
Node[] node = new Node[4];
node[0] = new Node(9);
node[1] = new Node(8);
node[2] = new Node(100);
node[3] = new Node(0);
Arrays.sort(node);
System.out.println(Arrays.toString(node));
}
}
运行结果:
[0, 8, 9, 100]
说明3点:
1.在Node类中的toString是只是我为了打印运行结果而重写的,与排序无关;
2.Node有多个属性时,当然可以根据需要二级排序,三级排序等等;
3.这样设计Node之后只能按照你定义的排序了,如果对一个Node要用不同的标准排序,请看下面:
3.2 使用Comparator比较器
使用Comparator就不像上面那样比较死板了,你可以设计Cmp1,Cmp2来满足你不同标准排序的要求比如下面是我随便举的例子,标准1:对人员先按照名字升序,如果名字相同按照年龄升序 ,标准2:先对年龄升序,如果年龄相同就按姓名升序
[c-sharp] view
plaincopy
import java.util.Arrays;
import java.util.Comparator;
class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return this.name;
}
public int getAge() {
return this.age;
}
@Override
public String toString() {
// TODO Auto-generated method stub
return "" + this.name + " " + this.age;
}
}
class Cmp1 implements Comparator {
@Override
public int compare(Object arg0, Object arg1) {
Person a = (Person) arg0;
Person b = (Person) arg1;
if (a.getName().compareTo(b.getName()) != 0)
return a.getName().compareTo(b.getName());
else {
if (a.getAge() < b.getAge())
return -1;
else if (a.getAge() > b.getAge())
return 1;
else
return 0;
}
}
}
class Cmp2 implements Comparator {
@Override
public int compare(Object arg0, Object arg1) {
Person a = (Person) arg0;
Person b = (Person) arg1;
if (a.getAge() < b.getAge())
return -1;
else if (a.getAge() > b.getAge())
return 1;
else
return a.getName().compareTo(b.getName());
}
}
public class Main {
public static void main(String[] args) {
Person[] p = new Person[4];
p[0] = new Person("ZZZ", 19);
p[1] = new Person("AAA", 109);
p[2] = new Person("AAA", 19);
p[3] = new Person("YYY", 100);
Arrays.sort(p, new Cmp1());
System.out.println("Cmp1");
System.out.println(Arrays.toString(p));
Arrays.sort(p, new Cmp2());
System.out.println("Cmp2");
System.out.println(Arrays.toString(p));
}
}
运行结果;
Cmp1
[AAA 19, AAA 109, YYY 100, ZZZ 19]
Cmp2
[AAA 19, ZZZ 19, YYY 100, AAA 109]
这种方法在CMP这个类设计好后,要排序时直接在Arrays.sort()中new一个CMP实例就好了,是不是很像c++中sort,或者qsort。由于这个类只在排序时使用,所以也可以设计成匿名内部类(只对Cmp1举例),代码如下;
[c-sharp] view
plaincopy
import java.util.Arrays;
import java.util.Comparator;
class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return this.name;
}
public int getAge() {
return this.age;
}
@Override
public String toString() {
return "" + this.name + " " + this.age;
}
}
public class Main {
public static void main(String[] args) {
Person[] p = new Person[4];
p[0] = new Person("ZZZ", 19);
p[1] = new Person("AAA", 109);
p[2] = new Person("AAA", 19);
p[3] = new Person("YYY", 100);
Arrays.sort(p, new Comparator<Object>() {
@Override
public int compare(Object arg0, Object arg1) {
Person a = (Person) arg0;
Person b = (Person) arg1;
if (a.getName().compareTo(b.getName()) != 0)
return a.getName().compareTo(b.getName());
else {
if (a.getAge() < b.getAge())
return -1;
else if (a.getAge() > b.getAge())
return 1;
else
return 0;
}
}
});
System.out.println(Arrays.toString(p));
}
}
匿名内部类是没有名字的内部类,你可以理解我把CMP这个类设计在了Arrays.sort()这个方法的参数中。你若java中这种机制不是很理解,就用前面那种吧。
4 “STL”
之所以对STL加引号,是因为JAVA中似乎不叫标准模板库,不过这个只是称呼,你知道我是在说这个就好了。很多前辈都提出了“STL伤代码能力论”我只是把它对应C++的那些列出来,用不用你看着办吧:
4.1 Sting 和StringBuffer
关于String我只想说这个是常量,创建了以后永远不会变的,想要变就用StringBuffer;这个有很多意想不到的方法,比如split方法可以根据给定的字符拆分字符串,方便吧,自己处理会麻烦死的还容易出错,再比如这两个都支持正则,正则的强大不需要我解释,另外还有很多,查api去吧~~~
4.2 List接口
就是表,可以用LinkedList类(可以理解链表实现所以增删比较快),ArrayList类(理解为数组实现访问比较快),当然还有大家很熟悉的Vector类;创建 List ll = new LinkedList();(你也可以LinkedList ll = new LinkedList();但推荐使用前者,因为在创建对象是,使用接口的引用指向对象是一个好习惯~)
方法: add(e), get(int index), clear(),isEmpty(),size()。。。(方法太多了,C++中实现的方法JAVA中差不多都有增无减地实现了,真的不想一一列出来,还是那句话,查api吧,真不行就留言)
JAVA集合框架中很多方法都是通用的,下面的有些方法就不再列举了;
4.3 Map接口
映射,有Hashtable类(Hashtable的同步版本),LinkedHashMap类(按元素插入顺序排序),HashMap类(比较常用),TreeMap类(按元素的自然顺序排序,这个的存储方式是传说中的红黑树~)可以实现;使用put方法添加元素,get(key)方法获得对应key的值,containsKey(key)方法判断是否已经存在此映射;
4.4 Set接口
集合,有HashSet类(没有排序),LinkedHashSet类(按插入顺序排序),TreeSet类(按自然序排序)可以实现;
4.5 Queue 接口
队列,用实现List的那些类实现好了,offer方法入队,poll方法取队头并扔掉头(若不想扔掉用peek方法);
4.6 PriorityQueue 类
优先队列,注意这个是类,所以创建时一定要PriorityQueue qp = new PriotityQueue();方法和Queue类似;
4.7 Stack类
栈,注意这个也是类,push压入栈,poll取栈顶并扔掉(同样peek取顶不扔掉);最后举一个例子吧HDOJ1873(这个例子可以看到这些“玩具”的数组怎么创建)
[c-sharp] view
plaincopy
import java.io.*;
import java.util.PriorityQueue;
public class Main {
public static class Patient implements Comparable {
int id, priority;
public Patient(int id, int priority) {
this.id = id;
this.priority = priority;
}
@Override
public int compareTo(Object arg0) {
// 设置病人的优先级
int priority = ((Patient) arg0).priority;
int id = ((Patient) arg0).id;
if (this.priority > priority)
return -1;
else if (this.priority < priority)
return 1;
else
return this.id - id;
}
}
public static void main(String[] args) throws IOException {
StreamTokenizer in = new StreamTokenizer(new BufferedReader(
new InputStreamReader(System.in)));
while (in.nextToken() != StreamTokenizer.TT_EOF) {
int n = (int) in.nval;
int id = 1;
PriorityQueue<Patient>[] pq = new PriorityQueue[3];
// PriorityQueue<Patient> 泛型,new PriorityQueue[3]后面就不要加<Patient>了
for (int i = 0; i < 3; ++i) {
pq[i] = new PriorityQueue<Patient>();
}
for (int i = 0; i < n; ++i) {
in.nextToken();
String flag = in.sval;
if (flag.equals("OUT")) {
// String的等值比较,不要用==呀!!!!!!!!
in.nextToken();
int doctor = (int) in.nval;
if (pq[doctor - 1].isEmpty())
// 是否为空
System.out.println("EMPTY");
else
System.out.println(pq[doctor - 1].poll().id);
// 取队头并扔掉
} else {
in.nextToken();
int doctor = (int) in.nval;
in.nextToken();
int priority = (int) in.nval;
pq[doctor - 1].offer(new Patient(id++, priority));
}
}
}
}
}
5 其他
想不好再写什么,看大家需要吧~
关于传值问题:
Java中对象(强调8种基本数据类型除外)一定是引用传递的,所以当你不需要引用传递时,就拷贝一份副本传进去,当然拷贝可以用clone(),具体写法看以下例子:
[c-sharp] view
plaincopy
public class Main {
public static class Point implements Cloneable{
int x, y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
@Override
public String toString() {
return "("+x+","+y+")";
}
}
static void fun(Point p) {
p.x += 1;
p.y += 1;
}
public static void main(String[] args) throws CloneNotSupportedException {
Point p1 = new Point(9,8);
System.out.print("p1:");
System.out.println(p1);
//p2 is a copy of p1
Point p2 = (Point) p1.clone();
fun(p2);
System.out.print("yeah~ if you convert p1's clone, p1 won't change");
System.out.println(p1);
System.out.print("if you convert p1 directly, p1 will change in main()");
fun(p1);
System.out.println(p1);
}
}
6后记
本文给出一些用java做acm的基本用法,一般不是“纯java选手”,学会上面这些应付应付比赛中“突如其来”的大数高精度之类java有明显优势的题目足够了。但是不要迷恋java,java只是一种语言,语言是规则,算法是思想;最后再次指出本文只谈java用于acm,且不推荐大家把java作为自己的第一语言玩acm,欢迎拍砖~~