题目3180:蓝桥杯2023年第十四届省赛真题-棋盘=========及=====探讨输入输出对竞赛的影响

原理链接:

题目3180:蓝桥杯2023年第十四届省赛真题-棋盘
https://www.dotcpp.com/oj/problem3180.html

第一次尝试:

package Dotcpp;

import java.util.Scanner;

public class 题目3180蓝桥杯2023年第十四届省赛真题__棋盘 {
	/*
	小蓝拥有 n × n 大小的棋盘
	小蓝进行了 m 次操作
	每次操作会将棋盘上某个范围内的所有棋子的颜色取反 (也就是白色棋子变为黑色,黑色棋子变为白色)。
	 */
	public static void main(String[] args) {
		//输入的第一行包含两个整数 n, m,
		//接下来 m 行每行包含四个整数 x1, y1, x2, y2,
		//对于所有评测用例,1 ≤ n, m ≤ 2000 ,1 ≤ x1 ≤ x2 ≤ n ,1 ≤ y1 ≤ y2 ≤ m 。
		Scanner sc =  new Scanner(System.in);
		int n = sc.nextInt();
		int m = sc.nextInt();
		int qipan [][] = new int[n][n];
		while(m-->0){
			int x1 = sc.nextInt();
			int y1 = sc.nextInt();
			int x2 = sc.nextInt();
			int y2 = sc.nextInt();
			Reverse(qipan,x1,y1,x2,y2);
		}//while(m-->=0)

		for (int i=0;i<n;i++){
			for (int j=0;j<n;j++){
				System.out.print(qipan[i][j]);
			}
			System.out.println();
		}
	}

	private static void Reverse(int[][] qipan, int x1, int y1, int x2, int y2) {
		for (int i=x1-1;i<x2;i++){
			for (int j=y1-1;j<y2;j++){
				if (qipan[i][j] == 0){
					qipan[i][j] = 1;
				}else{
					qipan[i][j] = 0;
				}
			}
		}
	}
}

如图所示:

题目3180:蓝桥杯2023年第十四届省赛真题-棋盘=========及=====探讨输入输出对竞赛的影响_第1张图片

题目3180:蓝桥杯2023年第十四届省赛真题-棋盘=========及=====探讨输入输出对竞赛的影响_第2张图片

必然会造成超时问题

结果

题目3180:蓝桥杯2023年第十四届省赛真题-棋盘=========及=====探讨输入输出对竞赛的影响_第3张图片
因此可以看到,该思路完全正确,但是会面临超时问题,必然不是一个很好的解决方案

解题思路:

private static void Reverse(int[][] qipan, int x1, int y1, int x2, int y2) {
		for (int i=x1-1;i<x2;i++){
			for (int j=y1-1;j<y2;j++){
				if (qipan[i][j] == 0){
					qipan[i][j] = 1;
				}else{
					qipan[i][j] = 0;
				}
			}
		}

核心代码如下,大家可以看出,此思路很简单,就是:

  1. 构造一个默认的数组,默认值都是0,刚刚好符合白色的概念
  2. 进行循环调整,遇到1则变成0,遇到0则变成1

第二次尝试

package Dotcpp;

import java.util.Scanner;

public class 题目3180蓝桥杯2023年第十四届省赛真题__棋盘_AC36 {
	public static void main(String[] args) {
		Scanner sc =  new Scanner(System.in);
		int n = sc.nextInt();
		int m = sc.nextInt();
		int qipan [][] = new int[2002][2002];
		while (m-->0){
			int x1 = sc.nextInt();
			int y1 = sc.nextInt();
			int x2 = sc.nextInt();
			int y2 = sc.nextInt();
			for (int i=x1;i<=x2;i++){
				qipan[i][y1]++;
				qipan[i][y2+1]--;
			}
		}

		for (int i=1;i<=n;i++){
			for (int j=1;j<=n;j++){
				qipan[i][j]+=qipan[i][j-1];
				System.out.print(qipan[i][j]%2);
			}
			System.out.println();
		}
	}
}

如图所示:

题目3180:蓝桥杯2023年第十四届省赛真题-棋盘=========及=====探讨输入输出对竞赛的影响_第4张图片
将整个棋盘拆开,每次x1,x2依然判断,但y1-y2不进行判断,而只是标记出起始位置
然后最后在通过累加,减少来判断取0/1.

结果(没有改变,很奇怪)

在这里插入图片描述
还是36超时

解题思路:

这个方法很巧妙,核心代码如上图圈出来的部分,
主要改进点如下:

1.不再对y1到y2进行遍历,将三重循环,改为二重循环
2.记录下每次变遍历的头y1,和结尾的下一个,即y2+1 ,然后对他们进行操作,方便后续的判别
3.最后就是qipan[i][j]+=qipan[i][j-1]; ,即进行累加求和,然后取模进行判断

照理说,这个代码应该已经可以通过了,但是最后的结果却和第一次提交的结果完全一致。
当时这里已经心态爆炸,百思不得其解啊。

第三次尝试-别人的代码::

import java.io.*;
import java.util.Arrays;
import java.util.Scanner;
 
/**
 * ClassName: F棋盘
 * Package:  com.蓝桥真题._14届
 * Describe:
 
3 3
1 1 2 2
2 2 3 3
1 1 3 3
 
ans:
001
010
100
 * @Create 2023/04/10 10:49
 */
public class Main {
    static int n,m;static int[][] map;
    public static void main(String[] args) throws Exception {
        n = nextInt(); m = nextInt(); map = new int[n + 4][n + 4];
        for(int [] row : map) Arrays.fill(row,1);
        //show();
 
        for(int i = 0; i < m; i++){ insert(nextInt(),nextInt(),nextInt(),nextInt(),-1); }
        //pw.println("---------------------"); show();
 
        for(int i =1; i <= n; i++){
            for(int j = 1; j <= n; j++){
                map[i][j] *= map[i - 1][j] * map[i][j-1] / map[i - 1][j - 1];
                pw.print(map[i][j] == 1 ? 0 : 1);
            }
            pw.println();
        }
 
        pw.flush();
    }
    static void insert(int x1,int y1,int x2,int y2,int q){
        map[x1][y1] *= q;
        map[x2 + 1 ][y1] /= q;
        map[x1][y2 +1 ] /= q;
        map[x2 + 1][y2 + 1] *= q;
    }
 
    static void show(){
        for(int[] row : map){
            for(int col : row)pw.printf("%3d",col);
            pw.println();
        }
    }
 
    static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    static StreamTokenizer st = new StreamTokenizer(br);
    static int nextInt() throws Exception {st.nextToken();return (int) st.nval;}
    static PrintWriter pw = new PrintWriter(new OutputStreamWriter(System.out));
}

评价

此方法仔细观察后,大家可以发现,其实相比上面的第一种和第二种造成了更多的冗余,且已经把数字由原来的0-1判断,改成了1和-1的判断,最后在通过

pw.print(map[i][j] == 1 ? 0 : 1);

这条语句把它改回判断

对应的结果

题目3180:蓝桥杯2023年第十四届省赛真题-棋盘=========及=====探讨输入输出对竞赛的影响_第5张图片

于是对别人的代码进行了研究改进

第一次:

//package Dotcpp;

import java.util.Arrays;
import java.util.Scanner;

public class Main {
	private static int n,m;
	private static int qipan [][] ;
	
	public static void main(String[] args) {

		Scanner sc =  new Scanner(System.in);
		n = sc.nextInt();
		m = sc.nextInt();
		qipan  = new int[n+2][n+2];
		for (int row [] : qipan){
			Arrays.fill(row,1);
		}

		while (m-->0){
			int x1 = sc.nextInt();
			int y1 = sc.nextInt();
			int x2 = sc.nextInt();
			int y2 = sc.nextInt();
			myInsert(x1,y1,x2,y2,-1,qipan);
		}

		for (int i=1;i<=n;i++){
			for (int j=1;j<=n;j++){
				qipan[i][j]*=qipan[i][j-1]*qipan[i][j-1]/qipan[i-1][j-1];
				System.out.print(qipan[i][j] == 1 ? 0:1);
			}
			System.out.println();
		}
		sc.close();

	}

	private static void myInsert(int x1, int y1, int x2, int y2, int q, int[][] qipan) {
		qipan[x1][y1]*=q;
		qipan[x2+1][y1] /=q;
		qipan[x1][y2+1] /=q;
		qipan[x2+1][y2+1] *=q;
	}


}

这一次只是单纯地把别人的实现代码给copy过来,套在自己的框架里,
当时感觉很奇怪,分析了一下,感觉还没有上述第二次尝试的设计思路好

对应结果

题目3180:蓝桥杯2023年第十四届省赛真题-棋盘=========及=====探讨输入输出对竞赛的影响_第6张图片
不错所料,答案还是超时很多,甚至出现了答案错误

要是细心地小伙伴可以自己多研究一下,就能看出来,有一个地方代码写错了,这里我就调皮一下,不圈出来,因为也不是本文的重点。

第二次:

//package Dotcpp;

import java.io.*;
import java.util.Arrays;
import java.util.Scanner;

public class Main {
	private static int n,m;
	private static int qipan [][] ;

	static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
	static StreamTokenizer st = new StreamTokenizer(br);
	static int nextInt() throws Exception {st.nextToken();return (int) st.nval;}
	static PrintWriter pw = new PrintWriter(new OutputStreamWriter(System.out));

	public static void main(String[] args) throws Exception {

		//Scanner sc =  new Scanner(System.in);
//		n = sc.nextInt();
//		m = sc.nextInt();
		n = nextInt();
		m = nextInt();
		
		qipan  = new int[n+2][n+2];
		for (int row [] : qipan){
			Arrays.fill(row,1);
		}

		while (m-->0){
			/*
			int x1 = sc.nextInt();
			int y1 = sc.nextInt();
			int x2 = sc.nextInt();
			int y2 = sc.nextInt();
			*/
			int x1 = nextInt();
			int y1 = nextInt();
			int x2 = nextInt();
			int y2 = nextInt();
			myInsert(x1,y1,x2,y2,-1);
		}

		for (int i=1;i<=n;i++){
			for (int j=1;j<=n;j++){
				qipan[i][j]*=qipan[i-1][j]*qipan[i][j-1]/qipan[i-1][j-1];
				System.out.print(qipan[i][j] == 1 ? 0:1);
			}
			System.out.println();
		}
		//sc.close();
		

	}

	private static void myInsert(int x1, int y1, int x2, int y2, int q) {
		qipan[x1][y1]*=q;
		qipan[x2+1][y1] /=q;
		qipan[x1][y2+1] /=q;
		qipan[x2+1][y2+1] *=q;
	}


}

改进:

1.引入了本文的另一个核心讨论板块:
题目3180:蓝桥杯2023年第十四届省赛真题-棋盘=========及=====探讨输入输出对竞赛的影响_第7张图片
2.改正了一下上面提到的一个小错误
题目3180:蓝桥杯2023年第十四届省赛真题-棋盘=========及=====探讨输入输出对竞赛的影响_第8张图片

3.只是改了部分的输入输出:
题目3180:蓝桥杯2023年第十四届省赛真题-棋盘=========及=====探讨输入输出对竞赛的影响_第9张图片
题目3180:蓝桥杯2023年第十四届省赛真题-棋盘=========及=====探讨输入输出对竞赛的影响_第10张图片

输出结果:

题目3180:蓝桥杯2023年第十四届省赛真题-棋盘=========及=====探讨输入输出对竞赛的影响_第11张图片
和前两次一样,并没有任何的区别,还是36超时。

第三次:

将输出也全部换成流式

//package Dotcpp;

import java.io.*;
import java.util.Arrays;
import java.util.Scanner;

public class Main {
	private static int n,m;
	private static int qipan [][] ;

	static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
	static StreamTokenizer st = new StreamTokenizer(br);
	static int nextInt() throws Exception {st.nextToken();return (int) st.nval;}
	static PrintWriter pw = new PrintWriter(new OutputStreamWriter(System.out));

	public static void main(String[] args) throws Exception {

		//Scanner sc =  new Scanner(System.in);
//		n = sc.nextInt();
//		m = sc.nextInt();
		n = nextInt();
		m = nextInt();
		
		qipan  = new int[n+2][n+2];
		for (int row [] : qipan){
			Arrays.fill(row,1);
		}

		while (m-->0){
			/*
			int x1 = sc.nextInt();
			int y1 = sc.nextInt();
			int x2 = sc.nextInt();
			int y2 = sc.nextInt();
			*/
			int x1 = nextInt();
			int y1 = nextInt();
			int x2 = nextInt();
			int y2 = nextInt();
			myInsert(x1,y1,x2,y2,-1);
		}

		for (int i=1;i<=n;i++){
			for (int j=1;j<=n;j++){
				qipan[i][j]*=qipan[i-1][j]*qipan[i][j-1]/qipan[i-1][j-1];
				//System.out.print(qipan[i][j] == 1 ? 0:1);
				pw.print(qipan[i][j] == 1 ? 0:1);
			}
			//System.out.println();
			pw.println();
		}
		pw.flush();
		//sc.close();
		
		

	}

	private static void myInsert(int x1, int y1, int x2, int y2, int q) {
		qipan[x1][y1]*=q;
		qipan[x2+1][y1] /=q;
		qipan[x1][y2+1] /=q;
		qipan[x2+1][y2+1] *=q;
	}


}

对应结果:

在这里插入图片描述
可以看到,全部AC

总结

通过这道题,大家可以深切的感受到流式输入的影响,在此我先贴出上述代码的输入格式,然后给出其他任意类型的输入形式,供大家以后使用

static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
	static StreamTokenizer st = new StreamTokenizer(br);
	static int nextInt() throws Exception {st.nextToken();return (int) st.nval;}
	static PrintWriter pw = new PrintWriter(new OutputStreamWriter(System.out));

第四次

于是我对之间的第一次尝试第二次尝试也改用文件流式输入。

第一次的改进

//package Dotcpp;

import java.io.*;
import java.util.Scanner;

public class Main {
	/*
	小蓝拥有 n × n 大小的棋盘
	小蓝进行了 m 次操作
	每次操作会将棋盘上某个范围内的所有棋子的颜色取反 (也就是白色棋子变为黑色,黑色棋子变为白色)。
	 */
	private static int n,m;
	private static int qipan [][] ;

	static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
	static StreamTokenizer st = new StreamTokenizer(br);
	static int nextInt() throws Exception {st.nextToken();return (int) st.nval;}
	static PrintWriter pw = new PrintWriter(new OutputStreamWriter(System.out));
	public static void main(String[] args) throws Exception {
		//输入的第一行包含两个整数 n, m,
		//接下来 m 行每行包含四个整数 x1, y1, x2, y2,
		//对于所有评测用例,1 ≤ n, m ≤ 2000 ,1 ≤ x1 ≤ x2 ≤ n ,1 ≤ y1 ≤ y2 ≤ m 。
		n = nextInt();
		m = nextInt();

		qipan = new int[n+4][n+4];
		//int qipan [][] = new int[n][n];
		while(m-->0){
//			int x1 = sc.nextInt();
//			int y1 = sc.nextInt();
//			int x2 = sc.nextInt();
//			int y2 = sc.nextInt();
			int x1 = nextInt();
			int y1 = nextInt();
			int x2 = nextInt();
			int y2 = nextInt();
			Reverse(x1,y1,x2,y2);
		}//while(m-->=0)

		for (int i=0;i<n;i++){
			for (int j=0;j<n;j++){
				//System.out.print(qipan[i][j]);
				pw.print(qipan[i][j]);
			}
			//System.out.println();
			pw.println();
		}
		pw.flush();
	}

	private static void Reverse(int x1, int y1, int x2, int y2) {
		for (int i=x1-1;i<x2;i++){
			for (int j=y1-1;j<y2;j++){
				if (qipan[i][j] == 0){
					qipan[i][j] = 1;
				}else{
					qipan[i][j] = 0;
				}
			}
		}
	}
}

第二次的改进

//package Dotcpp;

import java.io.*;
import java.util.Scanner;

public class Main {

	private static int n,m;
	private static int qipan [][] ;

	static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
	static StreamTokenizer st = new StreamTokenizer(br);
	static int nextInt() throws Exception {st.nextToken();return (int) st.nval;}
	static PrintWriter pw = new PrintWriter(new OutputStreamWriter(System.out));

	public static void main(String[] args) throws Exception {
	//	Scanner sc =  new Scanner(System.in);
//		int n = sc.nextInt();
//		int m = sc.nextInt();
		n = nextInt();
		m = nextInt();

		int qipan [][] = new int[n+4][n+4];
		while (m-->0){
//			int x1 = sc.nextInt();
//			int y1 = sc.nextInt();
//			int x2 = sc.nextInt();
//			int y2 = sc.nextInt();
			int x1 = nextInt();
			int y1 = nextInt();
			int x2 = nextInt();
			int y2 = nextInt();
			for (int i=x1;i<=x2;i++){
				qipan[i][y1]++;
				qipan[i][y2+1]--;
			}
		}

		for (int i=1;i<=n;i++){
			for (int j=1;j<=n;j++){
				qipan[i][j]+=qipan[i][j-1];
				//System.out.print(qipan[i][j]%2);
				pw.print(qipan[i][j]%2);
			}
			pw.println();
			//System.out.println();
		}
		pw.flush();
	}
}

对应结果

题目3180:蓝桥杯2023年第十四届省赛真题-棋盘=========及=====探讨输入输出对竞赛的影响_第12张图片

可以看到,全部AC!!!

因此我们下面探讨一下Scanner和文件流输入输出到底为什么差别这么大!

Scanner:

概述:JDK1.5后用于扩区用户的键盘输入;
原理:	Scanner(InputStream source)(构造一个新的Scanner,他声称的值是指定的输入流扫描的;)
		System类下面有一个静态的字段:public static fianl Inputstream in;(标准的输入流,对应着键盘录入)
		即:Inputstream in=Sysytem.in;
			Scanner scanner=new Scanner(in);
两套录入方法:
	1:录入数据:nextxxx(Scanner.nextLong)(Scanner.nextDouble)...
	2:录入字符串:nextLine()(String s=scanner.nextLine()
PS:使用nextLine()方法时,你先录入整数,在录入字符串时,录入不进去,需要在重新创建一个Scanner对象。
	hasnextXXX():判断录入数据的元素。

大家可以看到Scanner的本质是对用户键盘上的输入,进行一个一个的扫描。

IO流:

1. IO流介绍

IO流(Input Output Stream,输入输出流),表示数据在程序内存和磁盘之间的传输。按照数据流的流向不同分为输入、输出流,输入流表示程序从磁盘读入数据,输出流表示程序往磁盘写数据。按照数据读取、写的方式不同分为字节流和字符流,字节流(类名以Stream结尾)表示程序按字节读取数据,什么文件都可以读取;字符流表示程序按照字符方式读取,方便读取各种编码的文本文件,但是无法读取图片、音频及视频等文件。所有的流都实现了java.io.Closeable接口,都有close方法。所有的输出流都实现了java.io.Flushable接口,都有flush方法,字符流需要手动使用flush方法才会把数据写入磁盘。
————————————————
版权声明:本文为CSDN博主「原来的1024」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/yldmkx/article/details/116676428

题目3180:蓝桥杯2023年第十四届省赛真题-棋盘=========及=====探讨输入输出对竞赛的影响_第13张图片
IO流自然不用多说,粗暴点认为就是一次性读取全部的,整个文件里面的数据

Java StringTokenizer :

Java StringTokenizer 属于 java.util 包,用于分隔字符串!

  1. StringTokenizer(String str) :构造一个用来解析 str 的 StringTokenizer 对象。java 默认的分隔符是空格(“”)、制表符(\t)、换行符(\n)、回车符(\r)。

  2. StringTokenizer(String str, String delim) :构造一个用来解析 str 的 StringTokenizer 对象,并提供一个指定的分隔符。

  3. StringTokenizer(String str, String delim, boolean returnDelims) :构造一个用来解析 str 的 StringTokenizer 对象,并提供一个指定的分隔符,同时,指定是否返回分隔符。

题目3180:蓝桥杯2023年第十四届省赛真题-棋盘=========及=====探讨输入输出对竞赛的影响_第14张图片

总结:

以后大家遇到超时问题,也可以多考虑一下输入文件流:

老样子,先贴上本文的代码:

	
	static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
	
	static StreamTokenizer st = new StreamTokenizer(br);
	
	static int nextInt() throws Exception {st.nextToken();return (int) st.nval;}
	
	static PrintWriter pw = new PrintWriter(new OutputStreamWriter(System.out));
	

更多类型的文件输入输出流显示:

输入:

 private static char getChar() throws IOException {
        String s = getString();
        return s.charAt(0);
    }

    private static String getString() throws IOException {
        InputStreamReader inputStreamReader = new InputStreamReader(System.in);
        BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
        String str = bufferedReader.readLine();
        return str;
    }


    private static int getInt() throws IOException {
        String s = getString();
        return Integer.parseInt(s);

输出:
 	private static PrintWriter pw = new PrintWriter(new OutputStreamWriter(System.out));

你可能感兴趣的:(java,算法)