|
| 1 | +''' |
| 2 | +요구사항 |
| 3 | +- k (1~10)점을 어피치가 a발을 맞췄고, 라이언이 b발을 맞혔을 때, b> a이면 k점을 라이언이 가져가고, 아니면 어피치가 k점을 가져간다. 단 a = b = 0일 경우 누구도 k점을 가져가지 못한다. |
| 4 | +- 모든 과녁에 대하여 두 선수의 총점 계산, 단 최종 점수가 같으면 어피치를 우승자로 |
| 5 | +- 어피치가 n발을 쏜 후 그 정보를 가지고 라이언이 어피치를 가장 큰 점수차로 이기기 위해 n발을 어떻게 맞춰야 하는지 구해라. |
| 6 | +- 라이언이 우승할 수 없는 경우 -1을 출력하고, 라이언이 가장 큰 점수차로 이길 수 있는 방법이 여러개인 경우 가장 낮은 점수를 더 많이 맞힌 경우를 반환해야 한다. |
| 7 | +
|
| 8 | +
|
| 9 | +아이디어 |
| 10 | +- 이 문제는 greedy는 아닌거 같다. greedy로 하려면 큰 점수를 무조건 먹기 위해 어피치가 쏜 화살 + 1만큼 할 텐데 입출력 예 1번처럼 라이언이 10점을 먹어도 9점 8점을 어피치가 먹으면 라이언이 지게 된다. |
| 11 | +- 따라서 완전 탐색을 진행해야 하며, dfs와 backtracking을 사용하여 깊이 우선으로 모든 경우의 수 계산. |
| 12 | +- 양궁대회 시뮬레이션 아이디어: info(어피치의 결과 배열)가 10점부터 0점까지의 나열이므로 10점부터 0점까지 내림차순으로 화살을 쏜다. 모든 경우를 구할 수 없으므로 현재 점수에 대해 라이언이 0발 혹은 info[i] + 1발을 맞춘 경우만 고려하고 나머지 경우는 진행하지 않는다. 즉 어피치가 10점에 2발을 맞췄다면, 라이언은 0발을 맞추고 화살을 아껴 다음 점수로 가던지, 2+1발을 맞춰 화살을 쓰더라도 점수를 얻고 가던지 두가지 경우만 해야 한다. |
| 13 | +
|
| 14 | +
|
| 15 | +구현 |
| 16 | +1. dfs 진행 |
| 17 | + - dfs 종료 조건: 현재 점수판이 0점이거나, 남은 화살이 없을때 |
| 18 | + - dfs 진행: (점수 그대로, 화살개수 그대로) 혹은 (현재 점수 먹기, 화살개수 소모하기) 두가지 버전으로 나뉨 |
| 19 | +2. 라이언과 어피치 중 누가 얼마의 차이로 이겼는지 확인하는 함수 |
| 20 | + - 차이가 같다면 가장 낮은 점수를 더 많이 맞춘 배열 선택 |
| 21 | +
|
| 22 | +''' |
| 23 | + |
| 24 | +# 점수차가 같은 경우 가장 낮은 점수를 많이 쏜 것을 정답으로 |
| 25 | +def choose_answer(arr1, arr2): |
| 26 | + length = len(arr1) |
| 27 | + |
| 28 | + # 낮은 점수부터 체크해야 하니까 역순으로 |
| 29 | + for i in range(length-1, -1, -1): |
| 30 | + if arr1[i] > arr2[i]: |
| 31 | + return arr1 |
| 32 | + elif arr2[i] > arr1[i]: |
| 33 | + return arr2 |
| 34 | + |
| 35 | + # 반복문이 끝났는데 여기까지 온 경우는 두 배열이 같다는 뜻이므로 아무거나 반환 |
| 36 | + return arr1 |
| 37 | + |
| 38 | + |
| 39 | + |
| 40 | +# 승자와 점수차를 반환. |
| 41 | +def get_winner(lion, appeach): |
| 42 | + lion_score = 0 |
| 43 | + appeach_score = 0 |
| 44 | + length = len(appeach) |
| 45 | + for i in range(length): |
| 46 | + if lion[i] > 0 or appeach[i] > 0: |
| 47 | + # 라이언이 맞춘 화살이 어피치보다 많으면 라이언이 점수를 가져오고 |
| 48 | + if lion[i] > appeach[i]: |
| 49 | + lion_score += (10 -i) |
| 50 | + # 그렇지 않으면 어피치가 점수를 가져온다. |
| 51 | + else: |
| 52 | + appeach_score += (10 - i) |
| 53 | + |
| 54 | + # 라이언 승 |
| 55 | + if lion_score > appeach_score: |
| 56 | + return 1, lion_score - appeach_score |
| 57 | + else: |
| 58 | + return -1, appeach_score - lion_score |
| 59 | + |
| 60 | +def dfs(n, idx, lion, appeach): |
| 61 | + global max_result, answer |
| 62 | + |
| 63 | + # dfs 종료 조건. 현재 점수판이 0점이거나, 남은 화살이 없을 때 |
| 64 | + if n == 0 or idx == 10: |
| 65 | + # 점수판이 0점이면 라이언의 남은 화살 모두 0점에 맞춤 |
| 66 | + if idx == 10: |
| 67 | + lion[idx] = n |
| 68 | + |
| 69 | + # 둘 중 누가 승자인지 판단하고, 라이언이 승자면 최대점수인지 확인 |
| 70 | + winner, score_gap = get_winner(lion, appeach) |
| 71 | + |
| 72 | + # 라이언이 승자이면 |
| 73 | + if winner == 1: |
| 74 | + if max_result < score_gap: |
| 75 | + max_result = score_gap |
| 76 | + answer = lion |
| 77 | + elif max_result == score_gap: |
| 78 | + answer = choose_answer(answer, lion) |
| 79 | + return |
| 80 | + |
| 81 | + # 현재 점수를 포기하고 화살을 아껴서 다음 dfs 진행 |
| 82 | + dfs(n, idx+1, lion[:], appeach) |
| 83 | + |
| 84 | + # 어피치보다 한 발 더 쏴서 현재 점수를 먹고 다음 dsf 진행 |
| 85 | + if n >= appeach[idx] + 1: |
| 86 | + tmp = lion[:] |
| 87 | + tmp[idx] = appeach[idx] + 1 |
| 88 | + dfs(n - (appeach[idx] + 1), idx+1, tmp, appeach) |
| 89 | + |
| 90 | + |
| 91 | + |
| 92 | + |
| 93 | +def solution(n, info): |
| 94 | + global answer, max_result |
| 95 | + # 라이언이 맞춘 점수 초기화 |
| 96 | + lion = [0] * 11 |
| 97 | + # 정답이 될 배열 |
| 98 | + answer = [0] * 11 |
| 99 | + |
| 100 | + max_result = 0 |
| 101 | + |
| 102 | + # 10점부터 0점까지 점수판을 내림차순으로 순회하며 몇 발을 맞췄는지 시뮬레이션 하기 위함. |
| 103 | + # n은 남은 화살, idx는 현재 점수판(0이면 10점, 10이면 0점) |
| 104 | + dfs(n, 0, lion, info) |
| 105 | + |
| 106 | + if max_result == 0: |
| 107 | + print([-1]) |
| 108 | + else: |
| 109 | + print(answer) |
| 110 | + |
| 111 | + |
| 112 | +solution(10, [0,0,0,0,0,0,0,0,3,4,3]) |
0 commit comments