JAVA对拍食用说明
首先对拍是什么呢?
对拍是一种算法比赛里的技巧,能用一段对的代码检验另一段代码的对错。
那为什么需要对拍呢?
对拍这个技巧在OI里面比较重要,比如在考场上你写了正解代码还有多余的时间想判断对错,这个时候可以写一个一定对的暴力代码来和这个正解代码来对拍结果。写一个in类生成同样的随机样例分别给两段要对拍的代码,再用对拍类比较生成的结果是否相同,然后重复几千次这个操作都没问题那这个正解代码大概率就没问题(不会这么脸黑吧)。
哪怕不在OI比赛里,如果是在平时训练写题你有的代码你认为逻辑就是对的,但是就是过不了题目显示WA然后逐渐弘文(bushi)。这个时候可以去题解里找个正确的别人的代码拿过来来和你的拍一拍,再把结果不同的对应的输入显示出来来分析自己的错误。
怎么实现对拍呢?
讲完了对拍是什么和它的作用,那接下来就要讲讲怎么具体的对拍了,对拍这个技巧也有很多别的人也讲过,但是我搜了一下没有java对拍的板子,所以这里主要是java的对拍的代码,别的语言的实现方法也差不多也可以在网上找找。
接下来切入正题
对拍需要在同一文件夹下准备这么四个类:in,t1,t2,duipai分别是生成随机输入,暴力代码,要验证的代码,和调用另外三个类来验证的代码。
import java.util.Random;
public class in {
public static void main(String[] args) {
Random rand = new Random();
int n = rand.nextInt(10)+1;
System.out.println(n);
for (int i = 0; i < n; i++) {
int x = rand.nextInt(10);
System.out.print(x+" ");
}
}
}
in类这里模拟数组输入
这里选择数组里是否有相同元素为例子
import java.util.Scanner;
public class t1 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int[] a = new int[n];
for (int i = 0; i < n; i++) {
a[i] = sc.nextInt();
}
for (int i = 0; i < n; i++) {
for (int j = i + 1; j < n; j++) {
if (a[i] == a[j]) {
System.out.println("false");
return;
}
}
}
System.out.println("true");
}
}
一对一对暴力枚举O(n^2)
import java.util.HashSet;
import java.util.Scanner;
public class t2 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int[] a = new int[n];
for (int i = 0; i < n; i++) {
a[i] = sc.nextInt();
}
HashSet<Integer> hs = new HashSet<>();
for (int i = 0; i < n; i++) {
hs.add(a[i]);
}
if(hs.size() == 6){
System.out.println("error");
return;
}
if (hs.size() == n) {
System.out.println("true");
} else {
System.out.println("false");
}
}
}
//HashSet不保留相同的元素
用hashset的长度来验证O(n)
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
public class duipai {
public static void main(String[] args) throws InterruptedException, IOException {
int i = 1;
while (i <= 50) {
new ProcessBuilder("java", "-cp", "bin", "in").redirectOutput(new File("in.txt")).start().waitFor();
new ProcessBuilder("java", "-cp", "bin", "t1").redirectInput(new File("in.txt")).redirectOutput(new File("Main.out")).start().waitFor();
new ProcessBuilder("java", "-cp", "bin", "t2").redirectInput(new File("in.txt")).redirectOutput(new File("Main2.out")).start().waitFor();
int pd = new ProcessBuilder("fc", "/w", "Main.out", "Main2.out").start().waitFor();
if (pd == 0) {
System.out.printf("AC%d\n", i);
new File("in.txt").delete();
new File("Main.out").delete();
new File("Main2.out").delete();
} else {
System.out.printf("WA%d\n", i);
printfile("in.txt");
printfile("Main.out");
printfile("Main2.out");
}
i++;
}
}
static void printfile(String filename) throws IOException {
System.out.println(new String(Files.readAllBytes(new File(filename).toPath())).trim());
}
}
对拍代码调用in类的输出再输入到t1,t2再比较两个的输出 (这里的调用都是对应的class文件如果class存放路径不一样就修改bin为对应的路径位置,默认路径eclipse是bin,idea是target/classes) idea对应路径
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
public class duipai {
public static void main(String[] args) throws InterruptedException, IOException {
int i = 1;
while (i <= 50) {
new ProcessBuilder("java", "-cp", "target/classes", "in").redirectOutput(new File("in.txt")).start().waitFor();
new ProcessBuilder("java", "-cp", "target/classes", "t1").redirectInput(new File("in.txt"))
.redirectOutput(new File("Main.out")).start().waitFor();
new ProcessBuilder("java", "-cp", "target/classes", "t2").redirectInput(new File("in.txt"))
.redirectOutput(new File("Main2.out")).start().waitFor();
int pd = new ProcessBuilder("fc", "/w", "Main.out", "Main2.out").start().waitFor();
if (pd == 0) {
System.out.printf("AC%d\n", i);
new File("in.txt").delete();
new File("Main.out").delete();
new File("Main2.out").delete();
} else {
System.out.printf("WA%d\n", i);
printfile("in.txt");
printfile("Main.out");
printfile("Main2.out");
}
i++;
}
}
static void printfile(String filename) throws IOException {
System.out.println(new String(Files.readAllBytes(new File(filename).toPath())).trim());
}
}
(这里用fc指令只针对windows,别的系统可以用对应的指令或者不看平台的BufferedReader 手动读取文件内容并比对,不依赖系统命令但是麻烦点)
这时两个代码都对,对拍结果全是AC。
这里修改正解代码,假装自己写错了
import java.util.HashSet;
import java.util.Scanner;
public class t2 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int[] a = new int[n];
for (int i = 0; i < n; i++) {
a[i] = sc.nextInt();
}
HashSet<Integer> hs = new HashSet<>();
for (int i = 0; i < n; i++) {
hs.add(a[i]);
}
if(hs.size() == 6){
System.out.println("error");
return;
}
if (hs.size() == n) {
System.out.println("true");
} else {
System.out.println("false");
}
}
}
修改后n=6就会错误把对应的输入和输出打印出来,就拍出错误的输入样例了!
这里的输入的in类要根据具体题目来选择输入形式。数据的范围可以先大点来验证,如果发现结果不一样再切换小的n来得到输入来分析代码问题。 对拍类的while条件也可以参考in那种先大的验证再小的找具体的。
对拍大概就是这样了,这里也衍生出了一些别的东西,我们发现只要把对拍类改为只针对一个类来输出就变成了生成随机样例的代码(也可以自己对着样例看看逻辑对不对)
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
public class dabiao {
public static void main(String[] args) throws InterruptedException, IOException {
new ProcessBuilder("java", "-cp", "bin", "in").redirectOutput(new File("in.txt")).start().waitFor();
new ProcessBuilder("java", "-cp", "bin", "t1").redirectInput(new File("in.txt")).redirectOutput(new File("Main.out")).start().waitFor();
printfile("in.txt");
printfile("Main.out");
new File("in.txt").delete();
new File("Main.out").delete();
}
static void printfile(String filename) throws IOException {
System.out.println(new String(Files.readAllBytes(new File(filename).toPath())).trim());
}
}
如果有疑问或者错误请告诉我我及时修改,如果有帮到你请点点赞!