코딩 테스트 스터디/프로그래머스

[level 2] 양궁대회

남쪽마을밤송이 2022. 8. 10. 21:57

 문제 

https://school.programmers.co.kr/learn/courses/30/lessons/92342?language=python3 

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

 사용 언어 

Python3

 풀이 과정 

level 2 문제인데... 풀지 못했다😥

이번주에 코딩테스트 스터디에서 백트래킹을 공부한 이후, 이 문제에도 백트래킹을 사용하면 해결될 것 같아 다시 도전해보려고 했다. 그런데 어떻게 시작해야할지 감도 못잡고 실패..ㅎ

 

블로그를 참고하니 역시 완전 탐색 문제가 맞는 것 같고 구현 방법은 combination / BFS / DFS로 나뉘는 것 같았다.

 

나는 초반에 내가 풀고 싶었던 DFS 코드 느낌을 찾아서 약간 수정했다.

점수도 꽤 많이 주는 걸로 봐서 효율성 좋게 통과!

 

 제출 답안 

# 라이언과 어피치가 같은 점수에 같은 개수를 맞히면 어피치 득점
# k점 영역에 몇 개를 맞히든 k점만 획득
# 라이언이 가장 큰 점수 차이로 이길 때의 점수판

from copy import deepcopy
max_diff, max_board = 0, []

def solution(n, info):
    def dfs(ascore, lscore, cnt, idx, board):
        global max_diff, max_board
        if cnt > n:
            return
        if idx > 10:
            diff = lscore - ascore
            if diff > max_diff:
                board[10] = n - cnt
                max_board = board
                max_diff = diff
            return
        if cnt < n:
            board2 = deepcopy(board); board2[10-idx] = info[10-idx] + 1
            dfs(ascore, lscore+idx, cnt+info[10-idx]+1, idx+1, board2)
            
        board1 = deepcopy(board)
        
        if info[10-idx] > 0:
            dfs(ascore+idx, lscore, cnt, idx+1, board1)            
        else:
            dfs(ascore, lscore, cnt, idx+1, board1)
            
    dfs(0, 0, 0, 0, [0]*11)
    return max_board if max_board else [-1]

 

 다른 사람 풀이 

1. BFS

from collections import deque

def bfs(n, info):    
    res = []
    q = deque([(0, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])])
    maxGap = 0
    
    while q:
        focus, arrow = q.popleft()
        
        if sum(arrow) == n:  # 종료조건 1) 화살 다 쏜 경우
            apeach, lion = 0, 0
            for i in range(11):
                if not (info[i] == 0 and arrow[i] == 0):
                    if info[i] >= arrow[i]:
                        apeach += 10 - i
                    else:
                        lion += 10 - i
            if apeach < lion:  # 라이언이 이기면
                gap = lion - apeach
                if maxGap > gap:
                    continue
                if maxGap < gap:
                    maxGap = gap  # 최대점수차 갱신
                    res.clear()
                res.append(arrow)  # 최대점수차를 내는 화살상황 저장
        
        elif sum(arrow) > n:  # 종료조건 2) 화살 더 쏜 경우
            continue
        
        elif focus == 10:  # 종료조건 3) 화살 덜 쏜 경우
            tmp = arrow.copy()
            tmp[focus] = n - sum(tmp)
            q.append((-1, tmp))
        
        else:  # 화살 쏘기
            tmp = arrow.copy()
            tmp[focus] = info[focus]+1 
            q.append((focus+1, tmp))  # 어피치보다 1발 많이 쏘기
            tmp2 = arrow.copy()
            tmp2[focus] = 0
            q.append((focus+1, tmp2))  # 0발 쏘기
    return res

def solution(n, info):
    winList = bfs(n, info)
    
    if not winList:
        return [-1]
    elif len(winList) == 1:
        return winList[0]
    else:
        return winList[-1]

➡ deque와 while을 사용해서 정석 BFS처럼 풀고 조건을 잘 나눈 것 같다. 신기했는데 코드를 돌려보니 속도도 훨씬! 빨랐다. 확실히 DFS보단 BFS가 빠른 것 같다.

 

출처 : https://velog.io/@hygge/Python-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EC%96%91%EA%B6%81%EB%8C%80%ED%9A%8C-2022-KAKAO-BLIND-RECRUITMENT-BFS

 

2. Combination

from itertools import combinations_with_replacement
from collections import Counter

def solution(n, info):
    max_diff, max_comb_cnt = 0, {}

    for comb in combinations_with_replacement(range(11), n):
        cnt = Counter(comb)
        score1, score2 = 0, 0
        for i in range(1, 11):
            if info[10-i] < cnt[i]:
                score1 += i
            elif info[10-i] > 0:
                score2 += i
                
        diff = score1 - score2
        if diff > max_diff:
            max_comb_cnt = cnt
            max_diff = diff
            
    if max_diff > 0:
        answer = [0]*11
        for n in max_comb_cnt:
            answer[10-n] = max_comb_cnt[n] 
        return answer 
    else:
        return [-1]

➡ combination은 combination인데 처음 보는  combination_with_replacement 를 사용했다. 뭔지 궁금해서 찾아보니 iterable에서 원소 개수가 r개인 중복 조합 뽑기라고 한다. 새로운 거 하나 습득! 그런데 역시 예상했던대로 시간이 압도적으로 오래걸렸다. 완전 탐색은 알고리즘을 잘 사용하는 것이 중요한 것 같다!

 

출처 : https://velog.io/@jadenkim5179/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EC%96%91%EA%B6%81%EB%8C%80%ED%9A%8C 

 

3. DFS

max_num = -1
answer = []
    
def solution(n, info):

    def cal(appeach, lion):
        a_score, l_score = 0, 0
        for i in range(11):
            if appeach[i] == lion[i] == 0: continue
            elif appeach[i] > lion[i]:
                a_score += 10 - i
            else:
                l_score += 10 - i
        return a_score, l_score
    
    def dfs(lion, idx):
        global max_num, answer
        
        if idx == 11:
            a, l = 0, 0
            if sum(lion) == n:
                a, l = cal(info, lion)
            elif sum(lion) < n:
                lion[-1] += (n - sum(lion))
                a, l = cal(info, lion)
            else:
                return
            if a < l:
                if max_num < (l-a):
                    max_num = (l-a)
                    answer = [lion[:]]
                elif max_num == (l-a):
                    answer.append(lion[:])
            return
        
        lion.append(info[idx]+1)
        dfs(lion, idx+1)
        lion.pop()
        
        lion.append(0)
        dfs(lion, idx+1)
        lion.pop()
    
    dfs([], 0)
    if not answer: return [-1]

    answer.sort(key=lambda x: x[::-1], reverse=True)    
    return answer[0]

➡ 또다른 DFS를 활용한 풀이인데 정석  백트래킹 에 더 가깝고 코드 이해도 쉬웠다. append와 pop을 이용해 구현해야 했다는 걸 깨달았다. 그리고 같은 DFS를 사용했지만 내가 처음에 제출한 코드보다 훨씬 빨랐다.

'코딩 테스트 스터디 > 프로그래머스' 카테고리의 다른 글

[Level 2] 모음사전  (0) 2022.10.07
[level 2] 튜플  (0) 2022.09.29
[level 2] 주차 요금 계산  (0) 2022.08.01
[level 1] 비밀지도  (0) 2022.07.26
[level 3] N으로 표현  (0) 2022.07.24