[Gold IV] 뮤탈리스크 - 12869
성능 요약
메모리: 33300 KB, 시간: 64 ms
분류
너비 우선 탐색(bfs), 다이나믹 프로그래밍(dp), 그래프 이론(graphs), 그래프 탐색(graph_traversal)
문제 설명
수빈이는 강호와 함께 스타크래프트 게임을 하고 있다. 수빈이는 뮤탈리스크 1개가 남아있고, 강호는 SCV N개가 남아있다.
각각의 SCV는 남아있는 체력이 주어져있으며, 뮤탈리스크를 공격할 수는 없다. 즉, 이 게임은 수빈이가 이겼다는 것이다.
뮤탈리스크가 공격을 할 때, 한 번에 세 개의 SCV를 공격할 수 있다.
- 첫 번째로 공격받는 SCV는 체력 9를 잃는다.
- 두 번째로 공격받는 SCV는 체력 3을 잃는다.
- 세 번째로 공격받는 SCV는 체력 1을 잃는다.
SCV의 체력이 0 또는 그 이하가 되어버리면, SCV는 그 즉시 파괴된다. 한 번의 공격에서 같은 SCV를 여러 번 공격할 수는 없다.
남아있는 SCV의 체력이 주어졌을 때, 모든 SCV를 파괴하기 위해 공격해야 하는 횟수의 최솟값을 구하는 프로그램을 작성하시오.
입력
첫째 줄에 SCV의 수 N (1 ≤ N ≤ 3)이 주어진다. 둘째 줄에는 SCV N개의 체력이 주어진다. 체력은 60보다 작거나 같은 자연수이다.
출력
첫째 줄에 모든 SCV를 파괴하기 위한 공격 횟수의 최솟값을 출력한다.
풀이방법
핵심 조건과 출력해야 할 값을 먼저 살펴본다.
스타크래프트를 한 번쯤 해본 사람이라면 문제 이해는 굉장히 쉽게 할 수 있다. 뮤탈의 공격 패턴을 그대로 생각하면 되기 때문이다.
결국에 뮤탈은 공격 순서에 따라 scv의 체력을 9, 3, 1을 깍을 수 있기 때문에 모든 경우의 수를 따져보면 순열을 생각할 수 있다. 또한, scv를 모두 죽일 수 있는 공격 횟수의 최솟값을 구하는 문제이므로, dp 혹은 bfs도 생각해볼 수 있다.
나의 경우, dp가 약하기 때문에 풀이를 조금씩 찾아보며 내가 이해를 쉽게 할 수 있는 방식으로 문제를 풀었다.
1. 최대 세 개의 scv를 때릴 수 있고, 총 scv 개수가 3 최대 3개만 주어지기 때문에 dp를 수행할 리스트를 3차원 배열로 만들어준다. 각 차원의 배열은 각각 scv의 체력을 뜻한다. 체력의 default 값은 문제에 주어진 최대 체력 60보다 1 넘는 61로 넣어준다.
2. 순열을 이용하여 뮤탈이 공격할 수 있는 모든 경우의 수를 판단할 per함수를 만든다.
3. 모든 경우의 수로 계속 공격하다보면 모든 scv 체력이 0이 되는경우가 생기는데, 그 때의 최소 공격 횟수를 계속 초기화하며 최솟값을 찾아준다. ( 기저 부분 )
4. dp 배열의 인덱스를 벗어나지 않기 위해 scv 체력이 0 이하로 내려가면, 그 scv의 체력은 무조건 0으로 초기화 해준다.
5. scv들의 체력들에 저장된 값. 즉, 공격 횟수가 현재 함수가 실행하고 있는 횟수(cnt)보다 작거나 같을 경우는 기존에 default값으로 저장한 61보다 작은 수 이므로, 이미 방문한(공격했던 경우의 수) 곳이며 더 최소 공격 횟수로 때릴 수 있었던 경우이므로 더 이상 함수를 돌지 않고, 리턴해준다.
6. 2~5번을 재귀 호출하여 계속 실행하며 최종 값인 최소 공격 횟수(hp_max)를 출력하면 문제 해결.
from itertools import permutations as pt
def per(x, y, z, cnt):
global hp_max
if x <= 0 and y <= 0 and z <= 0: # scv를 모두 처치했을 때
hp_max = min(hp_max, cnt) # 처치한 최소 공격횟수 초기화
return
if x <= 0: x = 0
if y <= 0: y = 0
if z <= 0: z = 0
# 이미 방문했고, 지금보다 최소 공격횟수인 scv 체력들이면 리턴
if dp[x][y][z] <= cnt:
return
dp[x][y][z] = cnt
# 순열 : 뮤탈이 할 수 있는 모든 공격 방법으로 scv 체력을 깎는다
for i in pt([9,3,1], 3):
per(x - i[0], y - i[1], z - i[2], cnt + 1)
N = int(input())
scv = list(map(int,input().split()))
while len(scv) < 3:
scv += [0]
scv_max_hp = max(scv) # scv 중 최대 체력
hp_max = 61 # scv 체력이 될 수 없는 default 값
dp = [[[hp_max] * (scv_max_hp +1) for _ in range(scv_max_hp+1)] for _ in range(scv_max_hp+1)]
per(scv[0], scv[1], scv[2], 0)
print(hp_max)
'알고리즘' 카테고리의 다른 글
[Gold II] 빵집 - 3109 (DFS, greedy) (1) | 2023.02.21 |
---|---|
[Gold V] 인구 이동 - 16234 (그래프 탐색, BFS) : 복습 (2) | 2023.02.20 |
[Gold IV] 출근 경로 - 5569 (DP) (1) | 2023.02.19 |
[Silver V] 스네이크버드 - 16435 (그리디, 정렬) : JAVA, python (0) | 2023.02.18 |
[Silver I] 절댓값 힙 - 11286 (우선순위 큐) (1) | 2023.02.16 |