(Java) 백준 2798번 - 블랙잭

코딩너구리·2026년 1월 21일

코딩 문제 풀이

목록 보기
171/266

https://www.acmicpc.net/problem/2798

문제

> 카지노에서 제일 인기 있는 게임 블랙잭의 규칙은 상당히 쉽다. 
> 카드의 합이 21을 넘지 않는 한도 내에서, 카드의 합을 최대한 크게 만드는 게임이다. 
> 블랙잭은 카지노마다 다양한 규정이 있다.

> 한국 최고의 블랙잭 고수 김정인은 새로운 블랙잭 규칙을 만들어 상근, 창영이와 게임하려고 한다.

> 김정인 버전의 블랙잭에서 각 카드에는 양의 정수가 쓰여 있다.
> 그 다음, 딜러는 N장의 카드를 모두 숫자가 보이도록 바닥에 놓는다. 
> 그런 후에 딜러는 숫자 M을 크게 외친다.

> 이제 플레이어는 제한된 시간 안에 N장의 카드 중에서 3장의 카드를 골라야 한다. 
> 블랙잭 변형 게임이기 때문에, 플레이어가 고른 카드의 합은 M을 넘지 않으면서 M과 최대한 가깝게 만들어야 한다.

> N장의 카드에 써져 있는 숫자가 주어졌을 때, 
M을 넘지 않으면서 M에 최대한 가까운 카드 3장의 합을 구해 출력하시오.

접근

N장의 카드에서 임의로 3장을 뽑는다에서 백트래킹을 사용하였다. 카드를 뽑는 부분을 재귀함수로 정의했고 재귀의 탈출 부에서 M과 비교하여 M보다 작은 결과중 max연산을 통해 더 가장 큰 수를 갱신해 나간다.

문제해결

> StringTokenizer로 N과 M을 입력받아 저장해준다. 
> 카드에 적힌 수 들을 입력받고 N개의 카드를 배열로 만들어 각각에 저장한다.
> 카드를 뽑고 M과 비교하는 메소드인 BJ에 0번쨰 카드부터, 총합 0부터, 0장 뽑은 상태로 입력하여 호출한다.
> 블랙잭 BJ함수에서 for문으로 카드의 개수만큼 반복하는데 시작 값은 입력으로 들어온 idx의 값부터 시작한다. 
> 카드를 뽑아 sum에 누적하고 재귀로 들어간다.
> i+1번째 카드부터 시작하도록하여, 동일한 카드가 또 뽑히는 일 없게 한다.
> 바로 직전에 누적된 sum을 넘기고, 카드를 뽑았으므로 cnt에 1을 누적해 넘긴다.
> 쭉 재귀가 이루어지며 카드를 뽑다가 3장을 다 뽑고 4번쨰 카드를 뽑으려 재귀가 다시 들어가면
(이 때의 cnt는 3, 0부터 시작했기 때문에 0, 1, 2이기 때문이다.)
조건문에 걸려 지금까지의 sum과 M을 비교한다.
> M을 넘지 않는 수를 골라야 하므로 큰 수는 바로return을 맞고 돌아가고 넘지않으면 Math.max로 가장 큰 수를 갱신한다.
> 위 과정이 끝나고 return으로 돌아가면 sum-= 카드값을 통해 해당 자리에 다른 카드를 뽑을 수 있게, 제대로된 결과가 누적되게 해준다.

코드

import java.io.*;
import java.util.*;
import java.lang.*;

public class Main
{
    //2798번 블랙잭
    static int Max, N, M;
    static int[] card;

    public static void BJ(int idx, int sum, int cnt)
    {
        if(cnt >= 3)
        {
            if(sum <= M) Max = Math.max(Max, sum);
            return;
        }

        for(int i = idx; i < N; i++)
        {
            sum += card[i];
            BJ(i+1, sum, cnt+1);
            sum -= card[i];
        }

    }
    public static void main(String[] args) throws IOException
    {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine());

        N = Integer.parseInt(st.nextToken());
        M = Integer.parseInt(st.nextToken());

        st = new StringTokenizer(br.readLine());

        card = new int[N];
        for(int i = 0; i < N; i++) card[i] = Integer.parseInt(st.nextToken());

        BJ(0,0,0);
        System.out.println(Max);
    }
}

후기

예전엔 브루트포스가 더 쉬웠는데 지금은 백트래킹이 더 낫다. 어떻게 이걸 브루트포스로 풀지? 라는 생각이 들었다. 한번 고민해보자

0개의 댓글