🤖Algorithm

[백준] 15552 빠른 A+B Java

Jaeyoung Kim 2022. 8. 1. 11:43
728x90

빠른 A + B


문제

본격적으로 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를 한 줄에 하나씩 순서대로 출력한다.


예제입출력


시간제한

  • Java 8: 1.5 초
  • Java 8 (OpenJDK): 1.5 초

문제풀이

그간 다른 사람들의 백준 문제 풀이를 볼 때 마다 Scanner를 사용하는 사람보다 BufferReader를 사용하는 사람들이 많이 보였지만, 아직 내가 이해할 단계가 아니라는 생각이 들어 깊게 파고들지 않았었다.

하지만 이젠 알아야 될 때가 온 것 같다.

 

우선 가장 먼저 드는 의문

Scanner를 쓰는 것 보다 왜 BufferReader가, System.out.println을 쓰는 것보다 왜 BufferWriter가 더 빠를까?

버퍼를 통한 입출력 예시

위 이미지를 보더라도 바로 와닿지는 않는다.

버퍼를 사용하면 한 단계가 더 추가되는데 왜 더 빠르다는걸까?

현재 내가 바로 이해할 수 있는 수준에서 간단하게 작성해놓자면, 키보드라는 입력장치로 입력을 받은 'L'이라는 문자를 저장해두고, 다시 'O'라는 문자를 입력받아 저장하고 이 과정으로 "LOVE"라는 문자를 만들어 프로그램으로 출력시키는 것보다 중간에 버퍼를 두고 문자를 입력받을 때마다 날려둔 다음 한 번에 전송하는 것이 더 효율적이라고 한다.

 

건설 자재를 나르는데 하나씩 들고 현장으로 나르는 것보다 트럭에 실고 한 번에 나르는게 효율적인 것 처럼

 

그렇담 우선 버퍼가 빠르다는건 어떻게된 이해가 되었고, 코드는 어떻게 작성하는걸까

 

버퍼를 제대로 사용하기 위해서는 새로 3가지를 학습해야했다.

 - 입력을 받는 BufferReader
 - 출력을 담당하는 BufferWriter
 - BufferReader로 받은 값을 가공해줄 StringTokenizer

 

BufferReader

BufferReader를 사용하기 위해서는 3개의 import가 필요하며 main 함수 호출 시에도 예외 처리를 해줘야한다.

import java.io.BufferReader;
import java.io.IOException;
import java.io.InputStreamReader;

//하지만 import java.io.*;로 위 모두를 임포트해올 수 있다.

public class Main {
    public static void main(String[] args) throws IOException { //바로 이 부분
        BufferReader br = new BufferReader(new InputStreamReader(System.in));

        String str = br.readLing();              //BufferReader는 값을 String으로 받아오기에
        int A = Interger.parseInt(br.readLine()); //int 등을 쓰려면 타입을 변환해줘야한다.

        br.close(); //버퍼가 제 역할을 마치면 꼭 닫아주는 습관을 들이자
    }
}

 - BufferReader 및 BufferWriter 사용 시 꼭 close()로 마무리해줘야 하는 이유

 

 

구름EDU - 모두를 위한 맞춤형 IT교육

구름EDU는 모두를 위한 맞춤형 IT교육 플랫폼입니다. 개인/학교/기업 및 기관 별 최적화된 IT교육 솔루션을 경험해보세요. 기초부터 실무 프로그래밍 교육, 전국 초중고/대학교 온라인 강의, 기업/

edu.goorm.io

 

 

BufferWriter

import java.io.BuffferWriter;
import java.io.OutputStreamWriter;

//위 임포트도 마찬가지로 import.java.io.*;로 해결된다.

public class Main{
	public static void Main(String[] args) throws IOException {
    
    	BufferReader br = new BufferReader(new InputStreamReader(System.in));
        BuffferWriter bw = new BufferWriter(new OutputStreamWirter(System.out));//리더와 비슷하다
        
        int A = Integer.parseInt(br.readLine());
        
        bw.write(A); //출력은 이런 식으로
    }
}

 

StringTokenizer

BufferReader 클래스의 경우, 입력을 라인 단위로 읽어드릴 수 밖에 없다.

하지만 백준 문제를 비롯해서 여러 상황에서 입력을 받을 때 공백이나 문자열 등으로 구분해야하는 상황이 주어진다.

이러한 문제를 해결할 수 있는 방법 중 하나가 StringTokenizer이다.

직역하자면
String : 문자열을
Tokenizer : 토큰화한다.

 

StringTokenizer까지 적용하여 다음과 같은 코드로 문제를 해결하였다.

import java.io.*;
import java.util.StringTokenizer; //StringTokenizer는 이 import가 필요하다.

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));

        int T = Integer.parseInt(br.readLine());
        StringTokenizer st; //먼저 StringTokenizer를 선언한다.

        for(int i = 0; i < T; i++){
            st = new StringTokenizer(br.readLine()," "); //StringTokenizer는 BufferReader로 들어오는 값을 공백 기준으로 나눠줄거다
            int A = Integer.parseInt(st.nextToken()); //int A는 StringTokenizer로 받는 처음 값이다
            int B = Integer.parseInt(st.nextToken()); //int B는 StringTokenizer로 받는 두 번째 값이다.
            bw.write(A+B + "\n"); //for문을 돌며 값을 계속 받을건데, println과 달리 BufferWriter는 줄을 띄지 않는다. 고로 + "\n"을 통해 줄바꿈을 해준다
        }
        br.close();

        bw.flush(); // BufferWirter는 close 전에 한 번 flush를 통해 값을 비워줘야 오류를 방지할 수 있다.
        bw.close();;
    }
}

 

생각보다 간단해보이는 문제였지만 새로운 클래스들의 학습이 필요한 문제였다.

많이 배웠고, 익숙해지기 위해 앞으로 Scanner나 System.out.println 대신 Buffer들을 사용해봐야겠다.


Solution.java

import java.io.*;
import java.util.StringTokenizer;

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));

        int T = Integer.parseInt(br.readLine());
        StringTokenizer st;

        for(int i = 0; i < T; i++){
            st = new StringTokenizer(br.readLine()," ");
            int A = Integer.parseInt(st.nextToken());
            int B = Integer.parseInt(st.nextToken());
            bw.write(A+B + "\n");
        }
        br.close();

        bw.flush();
        bw.close();;
    }
}

 

 - 참고 / 출처

 

 

[JAVA 자바] StringTokenizer 클래스로 문자열 분리하기! split 비교.

안녕하세요 양햄찌 블로그 주인장입니다. 저번시간에는 split 함수를 이용해서 문자열을 나누는 방식을 알아봤는데요. 혹시 해당 포스팅이 궁금하신 분은 아래 링크를 참고해주세요 ▼ 자바 SPLIT

jhnyang.tistory.com

 

[Java 자바 입출력] BufferedReader/BufferedWriter

[자바 입출력 함수] BufferedReader / BufferWriter BufferedReader/BufferedWriter은 이름처럼 버퍼를 이용해서 읽고 쓰는 함수입니다. 이 함수는 버퍼를 이용하기 때문에 이 함수를 이용하면 입출력의 효율이..

jhnyang.tistory.com

 

728x90