키키의 개발일기 [Kiki's Dev Diary]

2798번: 블랙잭 (python) 본문

알고리즘/브루트 포스

2798번: 블랙잭 (python)

Yoozin 2023. 4. 19. 21:12

브루트포스 알고리즘으로 푼 블랙잭 문제

브루트포스 알고리즘 문제는 그냥 생각나는대로 구현해도 시간초과 안 떠서 좋다,,

 

브루트포스 알고리즘이 그냥 마구잡이로 다 탐색해서 구하는 알고리즘인 것으로 대충 알고 있었지만

같은 스터디원인 승현님이 스터디시간에 알려줘서 정확하게 뜻을 알게 됐다.

 

브루트(Brute): 무식한
포스(Force): 힘
"무식하게 탐색한다"

나 무식한 거 좋아 ㅜㅜ (머리 많이 안 써도 되잖아..)

이 알고리즘의 단점은 복잡도에 민감하고 탐색할 것들이 많으면 시간이 오래 걸린다는 것이다.

그래서 다른 알고리즘 문제를 이 방법으로 풀면 맨날 시간초과 뜸..

 

그럼 본론으로 돌아와 블랙잭 문제를 살펴보자. 

 

문제

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

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

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

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

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

입력

첫째 줄에 카드의 개수 N(3 ≤ N ≤ 100)과 M(10 ≤ M ≤ 300,000)이 주어진다. 둘째 줄에는 카드에 쓰여 있는 수가 주어지며, 이 값은 100,000을 넘지 않는 양의 정수이다.

합이 M을 넘지 않는 카드 3장을 찾을 수 있는 경우만 입력으로 주어진다.

출력

첫째 줄에 M을 넘지 않으면서 M에 최대한 가까운 카드 3장의 합을 출력한다.

접근방법

브루트포스 알고리즘이 아니었다면 꽤나 머리를 썼겠지만 문제를 보고 브루트포스이니 리스트 안을 for문 돌려서 세 개의 수들을 모두 더해보는 모든 경우의 수를 구하는 방법으로 풀면 되지 않을까 싶어서 for문을 세 번을 써서 구현해 보았다.

저 for문 세 개는 시간초과 날 거 같은 불안감을 주었지만 우선 써보았다.

for문으로 리스트c안의 숫자개수(a)만큼 돌려준다. 그리고 그 안에 for문을 넣어서 그 숫자 다음부터 돌려주고 또 for문 넣어줘서 그 숫자 다음부터 돌려주고 .. 

결국 만약 리스트[1,2,3,4,5]가 있다면 최초에는 c[i]가 1일 때 c[j]가 2고(i+1 부터니까) c[k]가 3이 된다. 이런 방식으로 세 개의 숫자들을 다 탐색할 수 있게 된다. 

여기서 t는 c[i],c[j],c[k]를 더한 값으로 그 전의 t값보다 크면 갱신된다. 왜냐면 n이랑 가장 가까우려면 n보다는 작거나 같지만 그 수들 중에 가장 커야하기 때문이다.

그래서 if문을 통해 그 리스트에 값들을 더한 값이 t를 넘지 않고 또한 n이 넘지 않아야한다는 조건을 주어서 통과하면 t를 갱신하게 된다. 

 

코드

a,n = map(int,input().split())
c = list(map(int,input().split()))
#n의 숫자를 넘지 않게 c리스트에서 3개 뽑기
t=0
for i in range(a):
    for j in range(i+1,a):
        for k in range(j+1,a):
            if c[i]+c[j]+c[k]>t and c[i]+c[j]+c[k]<=n:
                t=c[i]+c[j]+c[k]
               
print(t)

메모리 :31256KB   시간:68ms

 

 

 

하지만 for문을 세 번이나 쓴다니 복잡하다는 생각을 하였다.

그런 와중에 스터디원 영현님이 알려주신 꿀팁 → itertools를 사용하기

 

itertools에 있는 여러 함수들 중 combinations를 사용해 봤다.

combinations(iterable, r) : iterable에서 원소 개수가 r개인 조합 뽑기

 

itertools 사용한 코드

import itertools
a,n = map(int,input().split())
c = list(map(int,input().split()))
t=0
arr = list(itertools.combinations(c,3))
#입력 값이 5 21 / 5 6 7 8 9 일 때 arr은 [(5, 6, 7), (5, 6, 8), (5, 6, 9), (5, 7, 8), (5, 7, 9), (5, 8, 9), (6, 7, 8), (6, 7, 9), (6, 8, 9), (7, 8, 9)]
for i in arr:
    if i[0]+i[1]+i[2]>t and i[0]+i[1]+i[2]<=n:
        t=i[0]+i[1]+i[2]
                
print(t)

이렇게 해도 답이 제대로 나온다. 유용하게 쓰일 거 같음

 

메모리: 42904KB   시간: 94ms

시간은 더 걸렸다.,

 

'알고리즘 > 브루트 포스' 카테고리의 다른 글

1436번: 영화감독 숌 (python)  (0) 2023.04.21
Comments