카카오배 양궁대회가 열렸습니다.
라이언
은 저번 카카오배 양궁대회 우승자이고 이번 대회에도 결승전까지 올라왔습니다. 결승전 상대는 어피치
입니다.
카카오배 양궁대회 운영위원회는 한 선수의 연속 우승보다는 다양한 선수들이 양궁대회에서 우승하기를 원합니다. 따라서, 양궁대회 운영위원회는 결승전 규칙을 전 대회 우승자인 라이언에게 불리하게 다음과 같이 정했습니다.
어피치가 화살 n
발을 다 쏜 후에 라이언이 화살 n
발을 쏩니다.
점수를 계산합니다.
과녁판은 아래 사진처럼 생겼으며 가장 작은 원의 과녁 점수는 10점이고 가장 큰 원의 바깥쪽은 과녁 점수가 0점입니다.
만약, k(k는 1~10사이의 자연수)점을 어피치가 a발을 맞혔고 라이언이 b발을 맞혔을 경우 더 많은 화살을 k점에 맞힌 선수가 k 점을 가져갑니다. 단, a = b일 경우는 어피치가 k점을 가져갑니다.
k점을 여러 발 맞혀도 k점 보다 많은 점수를 가져가는 게 아니고 k점만 가져가는 것을 유의하세요. 또한 a = b = 0 인 경우, 즉, 라이언과 어피치 모두 k점에 단 하나의 화살도 맞히지 못한 경우는 어느 누구도 k점을 가져가지 않습니다.
- 예를 들어, 어피치가 10점을 2발 맞혔고 라이언도 10점을 2발 맞혔을 경우 어피치가 10점을 가져갑니다.
- 다른 예로, 어피치가 10점을 0발 맞혔고 라이언이 10점을 2발 맞혔을 경우 라이언이 10점을 가져갑니다.
모든 과녁 점수에 대하여 각 선수의 최종 점수를 계산합니다.
최종 점수가 더 높은 선수를 우승자로 결정합니다. 단, 최종 점수가 같을 경우 어피치를 우승자로 결정합니다.
현재 상황은 어피치가 화살 n
발을 다 쏜 후이고 라이언이 화살을 쏠 차례입니다.
라이언은 어피치를 가장 큰 점수 차이로 이기기 위해서 n
발의 화살을 어떤 과녁 점수에 맞혀야 하는지를 구하려고 합니다.
화살의 개수를 담은 자연수 n
, 어피치가 맞힌 과녁 점수의 개수를 10점부터 0점까지 순서대로 담은 정수 배열 info
가 매개변수로 주어집니다. 이때, 라이언이 가장 큰 점수 차이로 우승하기 위해 n
발의 화살을 어떤 과녁 점수에 맞혀야 하는지를 10점부터 0점까지 순서대로 정수 배열에 담아 return 하도록 solution 함수를 완성해 주세요. 만약, 라이언이 우승할 수 없는 경우(무조건 지거나 비기는 경우)는 [-1]
을 return 해주세요.
1 ≤ n
≤ 10
info
의 길이 = 11
info
의 원소 ≤ n
info
의 원소 총합 = n
info
의 i번째 원소는 과녁의 10 - i
점을 맞힌 화살 개수입니다. ( i는 0~10 사이의 정수입니다.)라이언이 우승할 방법이 있는 경우, return 할 정수 배열의 길이는 11입니다.
n
n
(꼭 n발을 다 쏴야 합니다.)10 - i
점을 맞힌 화살 개수입니다. ( i는 0~10 사이의 정수입니다.)[2,3,1,0,0,0,0,1,3,0,0]
과 [2,1,0,2,0,0,0,2,3,0,0]
를 비교하면 [2,1,0,2,0,0,0,2,3,0,0]
를 return 해야 합니다.[0,0,2,3,4,1,0,0,0,0,0]
과 [9,0,0,0,0,0,0,0,1,0,0]
를 비교하면[9,0,0,0,0,0,0,0,1,0,0]
를 return 해야 합니다.라이언이 우승할 방법이 없는 경우, return 할 정수 배열의 길이는 1입니다.
[-1]
을 return 해야 합니다.n | info | result |
---|---|---|
5 | [2,1,1,1,0,0,0,0,0,0,0] | [0,2,2,0,1,0,0,0,0,0,0] |
1 | [1,0,0,0,0,0,0,0,0,0,0] | [-1] |
9 | [0,0,1,2,0,1,1,1,1,1,1] | [1,1,2,0,1,2,2,0,0,0,0] |
10 | [0,0,0,0,0,0,0,0,3,4,3] | [1,1,1,1,1,1,1,1,0,0,2] |
입출력 예 #1
어피치와 라이언이 아래와 같이 화살을 맞힐 경우,
과녁 점수 | 어피치가 맞힌 화살 개수 | 라이언이 맞힌 화살 개수 | 결과 |
---|---|---|---|
10 | 2 | 3 | 라이언이 10점 획득 |
9 | 1 | 2 | 라이언이 9점 획득 |
8 | 1 | 0 | 어피치가 8점 획득 |
7 | 1 | 0 | 어피치가 7점 획득 |
6 | 0 | 0 | |
5 | 0 | 0 | |
4 | 0 | 0 | |
3 | 0 | 0 | |
2 | 0 | 0 | |
1 | 0 | 0 | |
0 | 0 | 0 |
어피치의 최종 점수는 15점, 라이언의 최종 점수는 19점입니다. 4점 차이로 라이언이 우승합니다.
하지만, 라이언이 아래와 같이 화살을 맞힐 경우 더 큰 점수 차로 우승할 수 있습니다.
과녁 점수 | 어피치가 맞힌 화살 개수 | 라이언이 맞힌 화살 개수 | 결과 |
---|---|---|---|
10 | 2 | 0 | 어피치가 10점 획득 |
9 | 1 | 2 | 라이언이 9점 획득 |
8 | 1 | 2 | 라이언이 8점 획득 |
7 | 1 | 0 | 어피치가 7점 획득 |
6 | 0 | 1 | 라이언이 6점 획득 |
5 | 0 | 0 | |
4 | 0 | 0 | |
3 | 0 | 0 | |
2 | 0 | 0 | |
1 | 0 | 0 | |
0 | 0 | 0 |
어피치의 최종 점수는 17점, 라이언의 최종 점수는 23점입니다. 6점 차이로 라이언이 우승합니다.
따라서 [0,2,2,0,1,0,0,0,0,0,0]
을 return 해야 합니다.
입출력 예 #2
라이언이 10점을 맞혀도 어피치가 10점을 가져가게 됩니다.
따라서, 라이언은 우승할 수 없기 때문에 [-1]
을 return 해야 합니다.
입출력 예 #3
어피치와 라이언이 아래와 같이 화살을 맞힐 경우,
과녁 점수 | 어피치가 맞힌 화살 개수 | 라이언이 맞힌 화살 개수 | 결과 |
---|---|---|---|
10 | 0 | 1 | 라이언이 10점 획득 |
9 | 0 | 1 | 라이언이 9점 획득 |
8 | 1 | 2 | 라이언이 8점 획득 |
7 | 2 | 3 | 라이언이 7점 획득 |
6 | 0 | 0 | |
5 | 1 | 2 | 라이언이 5점 획득 |
4 | 1 | 0 | 어피치가 4점 획득 |
3 | 1 | 0 | 어피치가 3점 획득 |
2 | 1 | 0 | 어피치가 2점 획득 |
1 | 1 | 0 | 어피치가 1점 획득 |
0 | 1 | 0 | 어피치가 0점 획득 |
어피치의 최종 점수는 10점, 라이언의 최종 점수는 39점입니다. 29점 차이로 라이언이 우승합니다.
하지만 라이언이 아래와 같이 화살을 맞힐 경우,
과녁 점수 | 어피치가 맞힌 화살 개수 | 라이언이 맞힌 화살 개수 | 결과 |
---|---|---|---|
10 | 0 | 1 | 라이언이 10점 획득 |
9 | 0 | 1 | 라이언이 9점 획득 |
8 | 1 | 2 | 라이언이 8점 획득 |
7 | 2 | 0 | 어피치가 7점 획득 |
6 | 0 | 1 | 라이언이 6점 획득 |
5 | 1 | 2 | 라이언이 5점 획득 |
4 | 1 | 2 | 라이언이 4점 획득 |
3 | 1 | 0 | 어피치가 3점 획득 |
2 | 1 | 0 | 어피치가 2점 획득 |
1 | 1 | 0 | 어피치가 1점 획득 |
0 | 1 | 0 | 어피치가 0점 획득 |
어피치의 최종 점수는 13점, 라이언의 최종 점수는 42점입니다. 이 경우도 29점 차이로 라이언이 우승합니다.
하지만, 첫 번째 경우와 두 번째 경우를 비교했을 때, 두 번째 경우가 두 경우 중 가장 낮은 점수인 4점을 더 많이 맞혔기 때문에 [1,1,2,3,0,2,0,0,0,0,0]
이 아닌 [1,1,2,0,1,2,2,0,0,0,0]
을 return 해야 합니다.
입출력 예 #4
가장 큰 점수 차이로 이기는 경우 중에서 가장 낮은 점수를 가장 많이 맞힌, 10~3점을 한 발씩 맞히고 나머지 두 발을 0점에 맞히는 경우인 [1,1,1,1,1,1,1,1,0,0,2]
를 return 해야 합니다.
않이.. 레벨 2 문제인데! 뭐가 이릏게 어렵게 느껴졌을까요!
문제만 읽었을 때에는 단순한 완전탐색 문제라고 생각했었는데... 생각보다 복잡한 문제였다구요.
아직은 제 레벨이 이 수준밖에 안되나 봅니다..
단순한 전략만 세우고, 멧돼지처럼 돌진하는 이 습관! 어떻게 고쳐야할까요!
단순한 저의 접근방식은 다음과 같았답니다.
(용어와 문장이 정리되있지 않아 혼란스러우실텐데, 나이브한 제 메모와 접근방식을 그-대로 보여드리고 싶어서! 그대로 옮겨보았답니다. 이 나이브한 메모조차 엄청 발전하는 과정을 지켜봐주세요..!)
- 완전탐색 재귀함수를 작성하고, n의 개수를 하나씩 감소시키면서, 11가지의 위치를 for 문으로 반복하며 재귀확인
- 모든 화살이 다 사용된 경우에, 어피치의 화살점수를 이길 수 있는지 확인
- 만약 점수를 이긴다면, 정답배열에 추가함, 만약 정답배열에 이미 답이 존재히면, 새로운 답과 크기비교 후 하나 선택
- 정답 리턴
점화식을 정립하고, 전체적으로 재귀를 돌아보니, 바아로 시간초과....
접근 방식이 잘못 되었던거죠!
흠...
곰곰히 생각해보면, 화살을 쏘는 방식 자체가 완전탐색을 하지 않아도 되는 것이였어요.
경우는 딱 두가지!
1. 남은 화살이 어피치의 특정 과녁의 명중 화살개수 + 1 보다 같거나 많을 경우
- 남은 화살을 어피치의 특정 과녁의 명중 화살개수 + 1 만큼 투자하여 점수를 뺏어온다.
- 그냥 포기하고 넘어간다
2. 남은 화살이 어피치의 특정 과녁의 명중 화살개수 + 1 보다 적을 경우
- 어차피 점수를 얻지 못하는 과녁점수이니, 그냥 넘어간다
이 인사이트를 얻지 못해서 완전탐색에 끙끙 했다니, 나는 멍춍이..
새 전략에 맞는 소스코드입니다.
def scoreCheck(a, b):
global answer
global gap
scoreA = 0
scoreB = 0
tmp = 10
for i in range(11):
if a[i] == b[i] and a[i] == 0:
tmp -= 1
continue
if a[i] >= b[i]:
scoreA += tmp
else:
scoreB += tmp
tmp -= 1
if scoreA < scoreB:
if gap > scoreB - scoreA:
return
elif gap == scoreB - scoreA:
for i in range(1, 11):
if answer[-i] < b[-i]:
answer = list(b)
break
elif answer[-i] > b[-i]:
break
else:
gap = scoreB - scoreA
answer = list(b)
def search(n, apitch, rion, idx):
if n == 0:
scoreCheck(apitch, rion)
return
# print(rion)
if idx == 10:
rion[idx] = n
search(0, apitch, rion, idx + 1)
rion[idx] = 0
else:
if apitch[idx] + 1 <= n:
rion[idx] = apitch[idx] + 1
search(n - (apitch[idx] + 1), apitch, rion, idx + 1)
rion[idx] = 0
search(n, apitch, rion, idx + 1)
def solution(n, info):
global answer
global gap
gap = 0
answer = []
arr = [0] * 11
search(n, info, arr, 0)
if sum(answer) == 0:
return [-1]
return answer
언제나 느끼는 문제점인데도, 잘 고쳐지지가 않는군요..
문제를 바로 해결하려들지 말고, 곰곰히 분석하고 인식하자. (제발)
다음번 문제에는 기필코 차분하게 접근하겠어요..! 😭