백준 #20 [C++] 15552. 빠른 A+B, cin.tie(NULL), sync_with_stdio(false)

K Choi·2022년 2월 8일
0

백준

목록 보기
20/37
post-thumbnail

❔문제:백준 15552

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

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

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

➡️입력

첫 줄에 테스트케이스의 개수 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

풀이

C 코드

#include <stdio.h>
int main() {
    int T, i, A, B;
    scanf("%d", &T);
    for (i=0; i<T; i++){
        scanf("%d %d", &A, &B);
        printf("%d\n", A+B);
    }
    return 0;
}

예제 입출력 보고 5줄 입력받고 5줄 나와야 하는 줄 알고 한참 고민했다....ㅋㅋ 근데 그냥 한 줄씩 입출력이었음..!

C++ 코드

#include <iostream>
using namespace std;
int main() {
    ios::sync_with_stdio(false);
    cin.tie(NULL);
    int T, i, A, B;
    cin >> T;
    for (i=0; i<T; i++){
        cin >> A >> B;
        cout << A+B << "\n";
    }
    return 0;
}

!!주의할 것!! \n은 꼭 ""로 감싸주기! 이거 때문에 컴파일 에러나서 수정해서 다시 제출함ㅎ;

지금까지 백준 단계별 순서에 따라 풀어왔던 문제들과 달리 이번 문제에서는 시간 단축을 위한 팁을 주고 있더라구요. 근데 이 팁 안에 처음 보는 녀석들이 언급되어 각각에 대해서 정리하고 넘어가겠습니다.

ios::sync_with_stdio(false);

우선 C++ 컴파일러에는 sync_with_stdio(true)가 default로 저장되어 있습니다. 이는 C++와 C를 혼용할 수 있도록 만들어주는 녀석이에요. 동기화를 시켜주는 셈이죠. 즉 아래와 같은 코드가 가능해집니다.

#include <iostream>
using namespace std;
int main() {
    int A;
    cin >> A;
    printf("%d", A);
    cout << "Hello";
    return 0;
}

위 코드를 실행해 A에 1을 입력하면 1Hello가 출력됩니다.

이번엔 코드에 ios::sync_with_stdio(false)를 넣어볼게요.

#include <iostream>
using namespace std;
int main() {
    ios::sync_with_stdio(false)
    int A;
    cin >> A;
    printf("%d", A);
    cout << "Hello";
    return 0;
}

이 코드는 실행해 A에 2를 입력하면 Hello2가 출력됩니다.
2가 앞에 나와야 할 것 같지만 순서가 달라졌습니다. 왜 이럴까요?


아래 코드도 살펴보겠습니다.

#include <iostream>
using namespace std;
int main(){
    int A;
    cin >> A;
    cout << a;
    printf("년 끝, ");
    cout << 2022;
    printf("년 ");
    printf("시작! ");
    cout << "굿 럭";
    return 0;
}

A에 2021을 입력하면 이 코드의 실행결과는 2021년 끝, 2022 시작! 굿 럭이 출력됩니다.

하지만 코드에 ios::sync_with_stdio(false);를 포함시키면
20212022굿 럭년 끝, 년 시작!이 출력됩니다.

ios::sync_with_stdio(false);가 포함되면
C++의 임시저장공간(독립 버퍼)를 사용해 속도가 빨라지는 장점은 있지만,
C와 혼용이 불가능해집니다. ios::sync_with_stdio(false);가 포함된 코드에서 C와 C++ 혼용시 보통 C++ 출력을 먼저 한 후 C 출력을 합니다.




이제 cin.tie(NULL); 또는 cout.tie(NULL)은 어떤 기능을 해주는지 살펴봅시다.

cin.tie(NULL); cout.tie(NULL);

프로그램은 버퍼가 가득 찰 때까지 기다리지 않고
개행 문자 입력시 버퍼가 자동으로 비워(flush)집니다.

cin과 cout이 묶여(tie)있을 때는 cin, cout을 계속 번갈아 써도 입출력이 동시에 나타납니다. 그런데 untie시키면, 즉 tie(NULL)하면 입력을 모두 받아야 출력이 됩니다.

이게 무슨 말이냐 하면..

cin과 cout은 각각 stream buffer를 지니는데, cin.tie(NULL)을 하면 cin 전에 cout의 stream buffer을 flush하지 않습니다.
즉, cout으로 무언가 출력하도록 한 후 다음 line에서 cin으로 입력을 받을 경우, 출력이 안 되었는데 입력을 받기 위해 대기하는 경우가 발생합니다. 왜냐하면 cout << ~~;는 stream buffer에서 console창으로 flush됨을 의미하지 않기 때문입니다. flush하려면 개행과 flush를 하는>>endl을 써줘야 합니다.

위의 긴 설명을 이해하기 위해 아래 두 코드를 살펴보겠습니다.
백준 15552 문제 풀이를 아래와 같이 작성해볼게요.

#include <iostream>
using namespace std;
int main() {
    ios::sync_with_stdio(false);
    cin.tie(NULL);
    cin.tie(NULL);
    int T, i, A, B;
    cin >> T;
    for (i=0; i<T; i++){
        cin >> A >> B;
        cout << A+B << '\n';
    }
    return 0;
}

이 코드의 실행 결과는 다음과 같은데,
3
1 1
2 3
5 5
2
5
10
(위 4줄은 입력, 아래 3줄은 출력)
즉, 출력이 안 되었는데 입력을 받았죠?


cin.tie(NULL);과 cout.tie(NULL);을 사용하면서 cout과 cin을 혼용해 사용하고 싶으면 어떻게 해야 할까요? 쌓여 있는 걸 flush를 사용해 비워주면 됩니다. 다만 느려지겠죠. 아래 코드로 살펴보겠습니다. ```cpp #include using namespace std; int main() { ios::sync_with_stdio(false); cin.tie(NULL); cin.tie(NULL); int T, i, A, B; cin >> T; for (i=0; i> A >> B; cout << A+B << flush <<'\n'; } return 0; } ``` 이 코드의 결과는 아래와 같습니다. 3 1 1 2 2 3 5 5 5 10 cin과 cout이 번갈아 나왔죠?
따라서 cin.tie(1)은 cin 전에 cout을 했다면 출력이 먼저 되고 입력이 나중에 되도록 합니다. 이렇게 하면 역시 cin이 cout의 stream buffer을 flush하는 동작이 없어지므로 동작 시간을 감소시키게 됩니다.




어떻게 보면 복잡한 내용 같지만 정말 많이 배우는 하루였네요!:)

profile
mbeCoder

0개의 댓글