2층 - 앞지름과 되새김의 장/백준 오답노트

java - 15552번 : 빠른 A+B(2024-01-06)

상이태상 2024. 1. 6. 03:47

1. 서론

반복문(for문)을 풀면서 이러한 문제를 만나게 된다.

 

문제

본격적으로 for문 문제를 풀기 전에 주의해야 할 점이 있다. 입출력 방식이 느리면 여러 줄을 입력받거나 출력할 때 시간초과가 날 수 있다는 점이다.

C++을 사용하고 있고 cin/cout을 사용하고자 한다면, cin.tie(NULL)과 sync_with_stdio(false)를 둘 다 적용해 주고, endl 대신 개행문자(\n)를 쓰자. 단, 이렇게 하면 더 이상 scanf/printf/puts/getchar/putchar 등 C의 입출력 방식을 사용하면 안 된다.

Java를 사용하고 있다면, Scanner와 System.out.println 대신 BufferedReader와 BufferedWriter를 사용할 수 있다. BufferedWriter.flush는 맨 마지막에 한 번만 하면 된다.

Python을 사용하고 있다면, input 대신 sys.stdin.readline을 사용할 수 있다. 단, 이때는 맨 끝의 개행문자까지 같이 입력받기 때문에 문자열을 저장하고 싶을 경우 .rstrip()을 추가로 해 주는 것이 좋다.

또한 입력과 출력 스트림은 별개이므로, 테스트케이스를 전부 입력받아서 저장한 뒤 전부 출력할 필요는 없다. 테스트케이스를 하나 받은 뒤 하나 출력해도 된다.

자세한 설명 및 다른 언어의 경우는 이 글에 설명되어 있다.

이 블로그 글에서 BOJ의 기타 여러 가지 팁을 볼 수 있다.

입력

첫 줄에 테스트케이스의 개수 T가 주어진다. T는 최대 1,000,000이다. 다음 T줄에는 각각 두 정수 A와 B가 주어진다. A와 B는 1 이상, 1,000 이하이다.

출력

각 테스트케이스마다 A+B를 한 줄에 하나씩 순서대로 출력한다.

예제 입력 1 복사

5
1 1
12 34
5 500
40 60
1000 1000

예제 출력 1 복사

2
46
505
100
2000

(출처 - https://www.acmicpc.net/problem/15552)

 

뭣????

여태까지 Scanner로 잘 풀다가 낯선이가 나타났다;;;

 

2. 본론

여태까지 풀고 찾아보고 chat GPT에게 물어본 결과 3가지 방법을 알 수 있었다.

 

1. Scanner를 이용하는 방법

 

import java.util.Scanner;

public class Main {

	public static void main(String[] args) {
		

		Scanner sc = new Scanner(System.in);
		System.out.println("테스트 케이스 입력");
		int T = sc.nextInt();
		
		for(int i=1; i<=T; i++) {
			int A = sc.nextInt();
			int B = sc.nextInt();
			
			System.out.println(A+B);
		}
		sc.close();
		}
}

 

<결과>

뭔가 입력받고 출력하기 가장 쉬운 방법이다.

 

2. BufferedReader와 BufferedWriter 사용하기(StringTokenizer 사용하기)

 

BufferedReaderScanner의 역할을 대신한다 라고 생각된다.

BufferedWriterSystem.out.println();의 역할을 대신한다 라고 생각된다.

 

StringTokenizer는 문자열(String)을 토큰화한다, 나눈다(Token)정도로 생각해볼 수 있다.

 

먼저 BufferedReader와 BufferedWriter를 선언해준다.

//		BufferedReader를 간단하게 말하자면 Scanner와 같은 역할을 한다고 생각됩니다.
//		BufferedReader는 Enter키를 경계로 인식합니다.
	
//		BufferedWriter를 간단하게 말하자면 System.out.print와 같은 역할을 한다고 생각됩니다.
//		BufferedReader, BufferedWriter의 선언
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); 
        	//이것을 선언함으로서 키보드로 치는 것을 받아들입니다.
            
		BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out)); 
        	//이것을 선언함으로서 BufferedReader로 받아들인 것을 출력합니다.

 

BufferedReader와 BufferedWriter는 문자열(String)에만 적용되기 때문에 따로 형변환을 해주어야 한다.

 

System.out.println("테스트 케이스 입력");
//	String T = br.readLine(); << 문자열일 경우, 그냥 이렇게 사용하면 됩니다.(BufferedReader로 받아들이는 것들은 보통 문자열입니다.)
int T = Integer.parseInt(br.readLine()); // << 형변환을 해주어야 합니다.

 

BufferedReader와 BufferedWriter은 입출력 작업을 수행하고 br.readLine()과 후에 나올 bw.write는 입출력 작업에 해당하는데 이는 작업 중에 예외가 발생할 수 있다. 그래서 예외를 던질 처리(throws IOException)를 해주어야 한다. 이제 까지 코드를 정리하면 다음과 같다.

 

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.StringTokenizer;

public class Buffered {

public static void main(String[] args) throws IOException {
		
//		BufferedReader를 간단하게 말하자면 Scanner와 같은 역할을 한다고 생각됩니다.
//		BufferedReader는 Enter키를 경계로 인식합니다.
	
//		BufferedWriter를 간단하게 말하자면 System.out.print와 같은 역할을 한다고 생각됩니다.
//		BufferedReader, BufferedWriter의 선언
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); //<<이것을 선언함으로서 키보드로 치는 것을 받아들입니다.
		BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out)); //<<이것을 선언함으로서 BufferedReader로 받아들인 것을 출력합니다.
		
		System.out.println("테스트 케이스 입력");
//		String T = br.readLine(); << 문자열일 경우, 그냥 이렇게 사용하면 됩니다.(BufferedReader로 받아들이는 것들은 보통 문자열입니다.)
		int T = Integer.parseInt(br.readLine()); // << 숫자형식일 경우, 문자형인 br.readLine()를 정수형, 실수형으로 변환해줄 필요가 생깁니다.
        
        	}
        }

 

이제 입력된 테스트 케이스 만큼 반복해야 한다.

 

for(int i=1;i<=T;i++) {
StringTokenizer st = new StringTokenizer(br.readLine()); //<< StringTokenizer는 말 그래도 문자열(String)을 토큰화(Tokenizer)합니다.
//			토큰화 라는 것은 split과 같은 역할을 한다고 볼 수 있겠습니다. 구분자가 많을 때 사용할 수 있을거라 생각합니다.
//			혹은 수식을 애매하게 알고 있을 때 사용하면 좋을 거라 생각됩니다.
			int A = Integer.parseInt(st.nextToken());
			int B = Integer.parseInt(st.nextToken());
			bw.write(A + B + "\n");	
			}
		
			bw.flush();
			bw.close();
			
			br.close();

BufferedWriter를 통해 버퍼를 잡아 뒀으니 flush와 close를 사용하여 버퍼에 있는 것을 모두 내보내고 종료시키고,

BufferedReader를 통해 버퍼를 잡아 뒀으니 close를 통해 종료시켜야 한다.

 

종합하면 이런 코드가 된다.

 

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.StringTokenizer;

public class Buffered {

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

		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
		
		System.out.println("테스트 케이스 입력");
		int T = Integer.parseInt(br.readLine());
		
		for(int i=1;i<=T;i++) {
        
			StringTokenizer st = new StringTokenizer(br.readLine());
            
			int A = Integer.parseInt(st.nextToken());
			int B = Integer.parseInt(st.nextToken());
			bw.write(A + B + "\n");	
			}
		
			bw.flush();
			bw.close();
			
			br.close();
		
		}
	
}

 

<결과>

 

여기서 "\n"에 대한 것은 개행문자라고 한다. 적용하지 않으면 출력값이 개행되지 않고 한줄로 출력될 것이다.

 

3. BufferedReader와 BufferedWriter 사용하기(sprit 사용하기)

 

StringTokenizer의 코드와 전체적으로 유사하지만 반복문 내에서 다르게 적용된다.

 

//			이건 StringTokenizer을 사용하지 않았을 때
			String[] input = br.readLine().split(" "); 
//			1. String[] input << String[] input String형(문자열) 배열을 생성합니다. 그리고 input 변수로 정의합니다.
//			2. br.readLine() << 키보드로 타이핑 되는 것을 받아들입니다.
//			2-1. .split(" "); 키보드로 타이핑 되는 것을 받아들이는데 배열에 넣기 위해서 구분이 필요합니다. split을 통해 공백(스페이스키)으로 구분합니다.
			
			int A = Integer.parseInt(input[0]);
//			input변수가 문자열(String)이기 때문에 정수형으로 변환해주는 작업이 필요하고, 공백(스페이스키)입력 전까지의 데이터를 받아들여 input배열 0번째칸에 넣습니다.
			
			int B = Integer.parseInt(input[1]);
//			input변수가 문자열(String)이기 때문에 정수형으로 변환해주는 작업이 필요하고, 공백(스페이스키)입력 후 데이터를 받아들여 input배열 1번째칸에 넣습니다.
//			Enter키를 누르면 첫번째 반복문이 끝납니다.

 

코드를 종합하면 다음과 같다.

 

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;

public class Buffered {

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

		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
		
		System.out.println("테스트 케이스 입력");
		int T = Integer.parseInt(br.readLine());
		
		for(int i=1;i<=T;i++) {
      
			String[] input = br.readLine().split(" "); 
			int A = Integer.parseInt(input[0]);
			int B = Integer.parseInt(input[1]);

			bw.write(A + B + "\n");	
			}
		
			bw.flush();
			bw.close();
			
			br.close();
		
		}
	
}

 

<결과>

 

3. 결론

 

쉬운 길로만 가고자 하는 주인장이라 Scanner를 애용할거 같지만....굳이 BufferedReader,Writer를 사용할거면?

StringTokenizer를 사용할거 같다.