일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- 코테연습
- 동적계획법
- python
- 카카오
- OS
- sql
- 배열
- C++
- Doitvue.js입문
- 리액트
- react
- dp
- LeetCode
- 프로그래머스
- CS
- 파이썬
- 웹프로그래밍
- 고득점Kit
- Level2
- web
- javascript
- 프로그래밍
- 백준
- Medium
- 자바스크립트
- Level1
- Level3
- typescript
- VUE
- 리트코드
- Today
- Total
[프로그래머스] 양궁대회 - python 본문
양궁대회
문제
현재 상황은 어피치가 화살 n발을 다 쏜 후이고 라이언이 화살을 쏠 차례입니다. 라이언은 어피치를 가장 큰 점수 차이로 이기기 위해서 n발의 화살을 어떤 과녁 점수에 맞혀야 하는지를 구하려고 합니다.
화살의 개수를 담은 자연수 n, 어피치가 맞힌 과녁 점수의 개수를 10점부터 0점까지 순서대로 담은 정수 배열 info가 매개변수로 주어집니다. 이때, 라이언이 가장 큰 점수 차이로 우승하기 위해 n발의 화살을 어떤 과녁 점수에 맞혀야 하는지를 10점부터 0점까지 순서대로 정수 배열에 담아 return 하도록 solution 함수를 완성해 주세요. 만약, 라이언이 우승할 수 없는 경우(무조건 지거나 비기는 경우)는 [-1]을 return 해주세요.
제한사항
- info의 길이 = 11
- 0 ≤ info의 원소 ≤ n
- info의 원소 총합 = n
- info의 i번째 원소는 과녁의 10 - i 점을 맞힌 화살 개수입니다. ( i는 0~10 사이의 정수입니다.)
- 라이언이 우승할 방법이 있는 경우, return 할 정수 배열의 길이는 11입니다.
- 0 ≤ return할 정수 배열의 원소 ≤ n
- return할 정수 배열의 원소 총합 = n (꼭 n발을 다 쏴야 합니다.)
- return할 정수 배열의 i번째 원소는 과녁의 10 - i 점을 맞힌 화살 개수입니다. ( i는 0~10 사이의 정수입니다.)
- 라이언이 가장 큰 점수 차이로 우승할 수 있는 방법이 여러 가지 일 경우, 가장 낮은 점수를 더 많이 맞힌 경우를 return 해주세요.
- 가장 낮은 점수를 맞힌 개수가 같을 경우 계속해서 그다음으로 낮은 점수를 더 많이 맞힌 경우를 return 해주세요.
- 예를 들어, [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 해야 합니다.
풀이
1 ~ 10 중에 중복해서 n번 선택하는 경우를 구했다. 중복 조합을 구하기 위해 combinations_with_replacement를 사용했다.
각 케이스 별로 lion 배열에 저장해서 점수 배열을 만든다.
for문을 돌면서 라이언과 어피치의 점수를 계산한 후 (점수차, 점수배열 문자열로 변환해서 뒤집은 것)으로 저장했다.
점수차가 같은 경우 낮은 점수를 더 많이 맞춘 경우를 리턴해야하기 때문에 점수 배열을 문자열로 만들어서 뒤집었다.
점수차, 점수 배열 모두 내림 차순으로 정렬해야해서 고민했다. 점수차야 - 붙이면 되지만 문자열은 - 붙여서 정렬할 수 없기 때문에.. 그래서 한 번에 오름차순으로 정렬한 다음 다시 뒤집어서 내림차순을 만들었다.
문자열로된 점수 배열을 다시 정수 배열로 만든 다음 뒤집어서 리턴했다. (처음에 넣을 때 뒤집어서 넣었으니까)
from itertools import combinations_with_replacement
def solution(n, info):
answer = []
scores = list(range(0, 11)) # 점수판
for c in combinations_with_replacement(scores, n): # 중복 조합 구하기
lion = [0] * 11 # 라이언 점수 배열
apeach_score = 0 # 라이언 점수
lion_score = 0 # 어피치 점수
for i in c: # 선택한 점수를 라이언 점수 배열에 표시
lion[i] += 1
for i in range(10):# 점수 계산
if lion[i] > info[i]: # 라이언이 더 많이 맞췄을 경우
lion_score += (10 - i)
elif info[i] != 0: # 어피치가 0 이상 쏴서 라이언보다 같거나 작게 맞췄을 때
apeach_score += (10 - i)
if lion_score > apeach_score: # 이긴 경우
answer.append((lion_score - apeach_score,''.join(map(str, lion[::-1])))) # 점수차, 라이언 점수 배열 뒤집은 문자열 저장
if not answer: # 이길 수 없는 경우
return [-1]
answer= sorted(answer, key=lambda x: (x[0], x[1]))[::-1] #오름차순으로 정렬한 후 뒤집어서 내림차순 만들기
return [int(i) for i in answer[0][1][::-1]] # 점수 배열 리턴
개선
통과는 했지만 너무 오래 걸려서 개선해봤다. 각 케이스를 구해서 Counter로 갯수를 센 후 따로 배열을 만들지 않고 점수 계산을 한다. 이 때 info 배열은 10-i로 점수가 표시되어 있기 때문에 10-i와 비교하면서 점수 계산을 한다.
점수 차가 클 때마다 갱신하면서 가장 점수 차가 큰 조합만 가져와서 answer에 넣는다.
같은 점수 차가 여러개 있을 경우 가장 첫 번째 조합만 들어가게 되는데 이 때 조합은 뒤에서 부터 숫자가 바뀌기 때문에 가장 낮은 점수를 가장 많이 맞힌 조합이 들어가게 되고 순서를 신경 쓰지 않아도 된다.
from itertools import combinations_with_replacement
from collections import Counter
def solution(n, info):
max_score = 0
max_combi = {}
answer = [0] * 11
for c in combinations_with_replacement(range(11), n):
lion = Counter(c)
apeach_score, lion_score = 0, 0
for i in range(11):
if info[10 - i] < lion[i]:
lion_score += i
elif info[10 - i] != 0:
apeach_score += i
if lion_score - apeach_score > max_score:
max_score = lion_score - apeach_score
max_combi = lion
if max_score == 0:
return [-1]
for i in range(11):
answer[10 - i] = max_combi[i]
return answer
DFS를 이용한 다른 사람 풀이
combinations_with_replacement를 사용할 경우 결국 모든 조합을 확인해봐야 하기 때문에 오래 걸린다. DFS를 이용해서 백트래킹해 가장 큰 조합일 때를 찾으면 더 빠르게 찾을 수 있다. 아 근데 lion이 아니라 ryan이었구나 ?
def solution(n, info):
global answer, max_score
def score(ryan):# 점수 차 리턴
s = 0
for i in range(11):
if ryan[i] == info[i] == 0:
continue
if ryan[i] > info[i]:
s += 10 - i
else:
s -= 10 - i
return s
def dfs(idx, left, ryan): # 과녁 점수 , 남은 화살, 라이언 배열
global answer, max_score
if idx == -1 and left:
return # n 만들 수 없는 경우
if left == 0: # n 만든 경우
s = score(ryan)
if max_score < s: # 현재 점수가 더 크면 업데이트
answer = ryan[:]
max_score = s
return
for i in range(left, -1, -1): # 10점부터 1개씩 쏴보기
ryan[idx] = i #10점 n개 쐈을 때, 10점 n-1개 쐈을 때 ,.. 로 분기함
dfs(idx-1, left-i, ryan) # 쏜 만큼 남은 화살에서 빼서 재귀
ryan[idx] = 0
answer = [0 for _ in range(11)]
max_score = 0
dfs(10, n, [0 for _ in range(11)])
return answer if max_score != 0 else [-1]
'코테 문제 풀이' 카테고리의 다른 글
[프로그래머스] 전력망을 둘로 나누기 - python (0) | 2022.04.23 |
---|---|
[프로그래머스] 올바른 괄호 - python (0) | 2022.04.23 |
[프로그래머스] 주차 요금 계산 - python (0) | 2022.04.21 |
[프로그래머스] k진수에서 소수 개수 구하기 - python (0) | 2022.04.21 |
[프로그래머스] N진수 게임 - python (0) | 2022.04.21 |