알고리즘 문제풀이/C++

[백준/C++] 2798번: 블랙잭 - 인간의 직관 vs 컴퓨터의 무식함 (브루트 포스)

aerimi-code 2026. 2. 4. 15:39

1. 문제 정보

  • 문제 번호: 2798번
  • 문제 이름: 블랙잭
  • 사용 언어: C++
  • 문제 링크: 백준 2798번 바로가기
  • 문제 요약:
  • N장의 카드 중 3장을 골라 그 합이 M을 넘지 않으면서 M에 최대한 가깝게 만드는 문제다.

 

2. 접근 방법 (알고리즘)

처음에는 "사람이 생각하는 방식"으로 접근했다.

가장 큰 카드를 먼저 고르고, 남은 값에 맞는 카드를 찾고... 안 맞으면 다시 뒤로 돌아가고... 하는 식으로 복잡한 로직을 짰다.

하지만 예외 처리가 너무 많아지고 코드가 꼬이기 시작했다.

이 문제의 N은 최대 100이다.

3장을 뽑는 경우의 수를 생각해보면  100 X 99 X 98 / 6 = 160,000번.

컴퓨터는 1초에 약 1억 번의 연산을 할 수 있다. 

따라서 복잡하게 머리 굴릴 필요 없이, 3중 반복문(for문)을 돌려 모든 경우의 수를 다 확인하는 "브루트 포스(Brute Force)" 방식이 가장 효율적이고 정확한 정답이다.


3. 코드 구현 (C++)

#include <iostream>
#include <vector>
#include <algorithm> // max 함수 사용
using namespace std;

int main() {
    // 입출력 속도 최적화
    ios::sync_with_stdio(false);
    cin.tie(NULL);

    int n, m;
    cin >> n >> m;
    
    vector<int> cards(n);
    for(int i = 0; i < n; i++){
        cin >> cards[i];
    }

    int maxSum = 0; // 정답을 담을 변수 (M을 넘지 않는 최대 합)

    // 3장의 카드를 뽑는 모든 경우의 수 탐색 (Brute Force)
    // i, j, k는 서로 다른 카드여야 하므로 범위를 겹치지하게 설정
    for(int i = 0; i < n; i++){
        for(int j = 0; j < i; j++){       // i보다 앞의 카드
            for(int k = 0; k < j; k++){   // j보다 앞의 카드
                
                int sum = cards[i] + cards[j] + cards[k];
                
                // 조건 1: 합이 M을 넘으면 안 됨
                // 조건 2: 현재까지 찾은 최대값(maxSum)보다 커야 갱신
                if(sum <= m){
                    maxSum = max(maxSum, sum);
                }
            }
        }
    }

    cout << maxSum;
    return 0;
}

 

 

4. 배운 점 & 회고 (TIL)

이번 문제는 해결하는 데 2~3시간이나 걸렸다. 코딩 자체보다 "생각의 전환"이 필요했기 때문이다.

A. 인간의 직관 vs 컴퓨터의 무식함

  • 처음 시도 (인간의 방식): "효율적으로 찾아야지"라는 생각에 while문과 if문을 남발하며 조건을 따졌다. 하지만 예외 상황(Edge Case)이 계속 나와서 복잡해졌다..
  • 수정된 시도 (컴퓨터의 방식): "그냥 다 해봐." 컴퓨터에게는 3중 for문도 순식간이다.
  • 교훈: 입력 크기(N)가 작다면(약 100~1000 이하), 복잡한 로직보다 단순 무식한 전체 탐색(Brute Force) (완전 탐색!!)이 훨씬 강력하고 버그 없는 해결책이다.

B. 로직이 막히면 돌아가라.

  • "방향이 안 보이면 아예 새로운 로직을 생각하자."
  • 복잡하게 짠 코드를 다 지우고, 단순한 반복문으로 다시 짰더니 허무할 정도로 금방 풀렸다.
  • 코드가 복잡해지고 있다면, 내가 문제를 어렵게 풀고 있는 건 아닌지 의심해봐야 한다.




💡 멘토의 코멘트


개발자들 사이에는 KISS 원칙 (Keep It Simple, Stupid)이라는 말이 있습니다. "복잡하게 짜서 버그를 만드느니, 단순하고 멍청하게 짜서 정답을 맞히는 게 낫다"는 뜻이죠. 이번에 겪은 "2-3시간의 삽질"이 앞으로 어려운 문제를 만났을 때 "일단 단순하게 생각해볼까?"라는 좋은 길잡이가 되어줄 겁니다. 화이팅!


 


 C++ 입출력 속도 향상

ios::sync_with_stdio(false);
cin.tie(NULL);
  • cin, cout의 속도를 비약적으로 높여주는 코드.
  • 백준에서 시간 초과를 방지하기 위해 main 함수 시작 부분에 관습적으로 넣는다.
  • 주의: 이 코드를 썼다면 printf, scanf와 섞어 쓰면 안 되고, endl 대신 \n을 써야 더 빠르다.

    ① ios::sync_with_stdio(false);
    • 의미: C언어의 표준 입출력(scanf, printf)과 C++의 표준 입출력(cin, cout)의 동기화(연결)를 끈다.
    • 원래는: C++은 C언어랑 섞어 써도 문제없게 하려고, cin을 쓸 때 scanf 눈치를 보고, cout을 쓸 때 printf 눈치를 보며 속도를 맞춥니다. 그래서 느려요.
    • 이걸 쓰면: "나(cin/cout)는 이제 독자적으로 달릴 거야!" 하고 연결을 끊어서 훨씬 빨라집니다.
    • ⚠️ 주의: 이걸 쓴 다음에는 scanf, printf와 섞어 쓰면 안 됩니다. (cin, cout만 써야 함)
    ② cin.tie(NULL);
    • 의미: cin과 cout의 묶임(tie)을 푼다.
    • 원래는: 입력(cin)을 받기 전에, 화면에 보여줄 게 있으면 출력(cout)부터 싹 비우고(flush) 입력을 받습니다. "입력받으려면 질문을 먼저 보여줘야지"라는 친절함 때문이죠.
    • 이걸 쓰면: "출력할 게 쌓여있든 말든 난 입력받을래." 하고 신경 끄게 합니다. 불필요한 출력 버퍼 비우기를 안 해서 빨라집니다.