[Python] Python 코딩테스트 Level 1

2024. 5. 19. 00:03코딩테스트

문제 설명

선물을 직접 전하기 힘들 때 카카오톡 선물하기 기능을 이용해 축하 선물을 보낼 수 있습니다. 당신의 친구들이 이번 달까지 선물을 주고받은 기록을 바탕으로 다음 달에 누가 선물을 많이 받을지 예측하려고 합니다.

  • 두 사람이 선물을 주고받은 기록이 있다면, 이번 달까지 두 사람 사이에 더 많은 선물을 준 사람이 다음 달에 선물을 하나 받습니다.
    • 예를 들어 A가 B에게 선물을 5번 줬고, B가 A에게 선물을 3번 줬다면 다음 달엔 A가 B에게 선물을 하나 받습니다.
  • 두 사람이 선물을 주고받은 기록이 하나도 없거나 주고받은 수가 같다면, 선물 지수가 더 큰 사람이 선물 지수가 더 작은 사람에게 선물을 하나 받습니다.
    • 선물 지수는 이번 달까지 자신이 친구들에게 준 선물의 수에서 받은 선물의 수를 뺀 값입니다.
    • 예를 들어 A가 친구들에게 준 선물이 3개고 받은 선물이 10개라면 A의 선물 지수는 -7입니다. B가 친구들에게 준 선물이 3개고 받은 선물이 2개라면 B의 선물 지수는 1입니다. 만약 A와 B가 선물을 주고받은 적이 없거나 정확히 같은 수로 선물을 주고받았다면, 다음 달엔 B가 A에게 선물을 하나 받습니다.
    • 만약 두 사람의 선물 지수도 같다면 다음 달에 선물을 주고받지 않습니다.

위에서 설명한 규칙대로 다음 달에 선물을 주고받을 때, 당신은 선물을 가장 많이 받을 친구가 받을 선물의 수를 알고 싶습니다.

친구들의 이름을 담은 1차원 문자열 배열 friends 이번 달까지 친구들이 주고받은 선물 기록을 담은 1차원 문자열 배열 gifts가 매개변수로 주어집니다. 이때, 다음달에 가장 많은 선물을 받는 친구가 받을 선물의 수를 return 하도록 solution 함수를 완성해 주세요.


제한사항
  • 2 ≤ friends의 길이 = 친구들의 수 ≤ 50
    • friends의 원소는 친구의 이름을 의미하는 알파벳 소문자로 이루어진 길이가 10 이하인 문자열입니다.
    • 이름이 같은 친구는 없습니다.
  • 1 ≤ gifts의 길이 ≤ 10,000
    • gifts의 원소는 "A B"형태의 문자열입니다. A는 선물을 준 친구의 이름을 B는 선물을 받은 친구의 이름을 의미하며 공백 하나로 구분됩니다.
    • A와 B는 friends의 원소이며 A와 B가 같은 이름인 경우는 존재하지 않습니다.
    •  

코드 프로세스 

  1. 데이터 구조 설정: 각 친구별로 준 선물 수와 받은 선물 수를 저장합니다.
  2. 선물 지수 계산: 선물 지수 = 준 선물 수 - 받은 선물 수
  3. 선물 기록 분석: 모든 선물 기록을 검토하여 각 친구가 준 선물과 받은 선물의 수를 업데이트합니다.
  4. 선물 예상 수 계산:
    • 친구가 다른 친구에게 선물을 더 많이 준 경우: 그 친구에게 선물을 받습니다.
    • 선물을 주고받은 기록이 없거나 같은 수의 선물을 주고 받았다면: 선물 지수가 더 높은 친구가 선물을 받습니다.
    • 선물 지수도 같다면: 선물을 주고받지 않습니다.
  5. 결과 도출: 각 친구가 받을 선물의 수를 계산한 후, 가장 많이 선물을 받는 친구의 선물 수를 반환합니다.

from collections import defaultdict


def solution(friends, gifts):
    answer = 0
    # 선물을 주고받은 기록
    history = {
        friend: {"gift": defaultdict(int), "received": defaultdict(int), "rank": 0}
        for friend in friends
    }
    set_friends = set(friends)

    # 선물 지수
    def history_index(friend):
        x = sum(history[friend]["gift"].values())
        y = sum(history[friend]["received"].values())
        return x - y

    # 선물을 주고받지 않은 사람
    def history_difference(friend):
        y = set(
            list(history[friend]["gift"]) + list(history[friend]["received"]) + [friend]
        )
        return set_friends - y

    # 선물 기록 갱신
    for gift in gifts:
        giver, recipient = gift.split()
        history[giver]["gift"][recipient] += 1
        history[recipient]["received"][giver] += 1

    # 받은 선물 계산
    for friend in friends:
        pivot_index = history_index(friend)
        pivot_difference = history_difference(friend)

        # 선물을 주고받은 기록이 하나도 없을때 처리
        if pivot_difference:
            for name in pivot_difference:
                if pivot_index > history_index(name):
                    history[friend]["rank"] += 1

        # 선물을 주고받은 기록이 있을때 처리
        for giver, give_cnt in history[friend]["gift"].items():
            if giver in history[friend]["received"]:
                received_cnt = history[friend]["received"][giver]
                if give_cnt > received_cnt or (
                    give_cnt == received_cnt and pivot_index > history_index(giver)
                ):
                    history[friend]["rank"] += 1
            else:
                history[friend]["rank"] += 1

        # 가장 많이 선물받은 값 저장
        answer = max(answer, history[friend]["rank"])

    return answer
  1. 변수 초기화 및 구조 설정:
    • history: 각 친구 이름을 키로 하고, 그 친구가 다른 친구들에게 선물을 준 횟수(gift), 다른 친구들로부터 선물을 받은 횟수(received), 그리고 이 친구의 순위(rank)를 값으로 하는 사전을 포함하는 사전을 생성한다. 여기서 gift와 received는 defaultdict(int)를 사용하여 친구 이름을 키로, 선물 주고받은 횟수를 값으로 저장한다.
    • set_friends: 모든 친구들의 이름을 요소로 하는 집합을 생성한다. 이 집합은 후에 선물을 주고받지 않은 친구들을 확인할 때 사용된다.
  2. 선물 기록 갱신:
    • gifts 리스트를 순회하면서 각 선물 정보를 파싱한다. 각 선물 정보는 "giver recipient" 형태의 문자열로 구성되어 있으며, giver는 선물을 준 사람, recipient는 선물을 받은 사람을 나타낸다.
    • 선물을 준 사람의 gift 사전과 받은 사람의 received 사전에 해당 정보를 갱신한다.
  3. 핵심 함수 설명:
    • history_index(friend): 특정 친구의 선물 지수를 계산한다. 선물 지수는 해당 친구가 준 선물의 총 수에서 받은 선물의 총 수를 뺀 값으로, 선물을 더 많이 준 친구일수록 높은 값을 갖는다.
    • history_difference(friend): 특정 친구와 선물을 주고받지 않은 친구들의 목록을 찾는다. 이는 친구가 아직 선물을 주거나 받지 않은 모든 다른 친구들의 집합을 반환한다.
  4. Rank 계산 로직:
    • 각 친구에 대하여 그 친구의 선물 지수(pivot_index)와 선물을 주고받지 않은 친구들의 목록(pivot_difference)을 계산한다.
    • 만약 pivot_difference 집합에 요소가 있다면, 이는 아직 교류가 없는 친구들이 존재한다는 것을 의미한다. 이 경우, 현재 친구의 선물 지수가 다른 친구의 선물 지수보다 높으면, 현재 친구의 'rank'를 증가시킨다.
    • 이미 선물을 주고 받은 기록이 있는 친구들에 대해서는, 각각 주고받은 선물의 수를 비교하고, 또한 선물 지수를 비교하여 rank를 조정한다.
  5. 결과 반환:
    • 모든 친구들의 rank 중에서 가장 높은 값을 answer에 저장하고 반환한다. 이는 가장 많이 선물을 주거나 더 높은 선물 지수를 가진 친구를 찾는 것을 의미한다.

함수 및 딕셔너리 정리

1. 딕셔너리: history

  • 목적: 각 친구별로 선물을 준 횟수와 받은 횟수를 추적합니다.
  • 구조:
    • Key: 친구의 이름 (friend)
    • Value: 또 다른 딕셔너리를 포함하여, 이 딕셔너리는 두 개의 defaultdict(int)를 포함합니다:
      • 'given': 해당 친구가 다른 친구에게 준 선물의 횟수를 저장합니다. 여기서 key는 받는 친구의 이름, value는 준 횟수입니다.
      • 'received': 해당 친구가 다른 친구로부터 받은 선물의 횟수를 저장합니다. 여기서 key는 주는 친구의 이름, value는 받은 횟수입니다.

2. 함수: solution(friends, gifts)

  • 목적: 친구들의 목록과 선물 기록을 입력받아 다음 달에 가장 많이 선물을 받을 친구의 받을 선물 수를 계산하여 반환합니다.
  • 파라미터:
    • friends: 친구들의 이름을 포함하는 리스트.
    • gifts: 선물 거래 기록을 나타내는 문자열 리스트, 각 문자열은 "giver receiver" 형식입니다.
  • 처리 과정:
    1. 친구별 선물 기록을 저장할 history 딕셔너리를 초기화합니다.
    2. 주어진 gifts 리스트를 순회하면서 각 선물 기록을 분석하여 history 딕셔너리를 갱신합니다.
    3. 각 친구에 대해 다른 모든 친구와의 선물 교환 및 선물 지수를 기반으로 다음 달에 받을 선물의 수를 계산합니다.
    4. 계산된 선물 수 중 최대값을 반환합니다.

3. 딕셔너리: gifts_to_receive

  • 목적: 각 친구가 다음 달에 받을 선물의 수를 저장합니다.
  • 구조:
    • Key: 친구의 이름
    • Value: 해당 친구가 다음 달에 받을 선물의 수 (정수)

2번문제

[PCCP 기출문제] 1번 / 붕대 감기

 

문제 설명

어떤 게임에는 붕대 감기라는 기술이 있습니다.

붕대 감기는 t초 동안 붕대를 감으면서 1초마다 x만큼의 체력을 회복합니다. t초 연속으로 붕대를 감는 데 성공한다면 y만큼의 체력을 추가로 회복합니다. 게임 캐릭터에는 최대 체력이 존재해 현재 체력이 최대 체력보다 커지는 것은 불가능합니다.

기술을 쓰는 도중 몬스터에게 공격을 당하면 기술이 취소되고, 공격을 당하는 순간에는 체력을 회복할 수 없습니다. 몬스터에게 공격당해 기술이 취소당하거나 기술이 끝나면 그 즉시 붕대 감기를 다시 사용하며, 연속 성공 시간이 0으로 초기화됩니다.

몬스터의 공격을 받으면 정해진 피해량만큼 현재 체력이 줄어듭니다. 이때, 현재 체력이 0 이하가 되면 캐릭터가 죽으며 더 이상 체력을 회복할 수 없습니다.

당신은 붕대감기 기술의 정보, 캐릭터가 가진 최대 체력과 몬스터의 공격 패턴이 주어질 때 캐릭터가 끝까지 생존할 수 있는지 궁금합니다.

붕대 감기 기술의 시전 시간, 1초당 회복량, 추가 회복량을 담은 1차원 정수 배열 bandage와 최대 체력을 의미하는 정수 health, 몬스터의 공격 시간과 피해량을 담은 2차원 정수 배열 attacks가 매개변수로 주어집니다. 모든 공격이 끝난 직후 남은 체력을 return 하도록 solution 함수를 완성해 주세요. 만약 몬스터의 공격을 받고 캐릭터의 체력이 0 이하가 되어 죽는다면 -1을 return 해주세요.


제한사항
  • bandage는 [시전 시간, 초당 회복량, 추가 회복량] 형태의 길이가 3인 정수 배열입니다.
    • 1 ≤ 시전 시간 = t ≤ 50
    • 1 ≤ 초당 회복량 = x ≤ 100
    • 1 ≤ 추가 회복량 = y ≤ 100
  • 1 ≤ health ≤ 1,000
  • 1 ≤ attacks의 길이 ≤ 100
    • attacks[i]는 [공격 시간, 피해량] 형태의 길이가 2인 정수 배열입니다.
    • attacks는 공격 시간을 기준으로 오름차순 정렬된 상태입니다.
    • attacks의 공격 시간은 모두 다릅니다.
    • 1 ≤ 공격 시간 ≤ 1,000
    • 1 ≤ 피해량 ≤ 100

  1. 붕대 감기 기술:
    • 시전 시간 (t): 기술을 연속적으로 사용할 수 있는 시간 (초)
    • 초당 회복량 (x): 매초 회복하는 체력의 양
    • 추가 회복량 (y): 연속적으로 t초 동안 기술을 사용했을 때 추가로 회복되는 체력의 양
  2. 최대 체력 (health): 캐릭터의 최대 체력. 회복량이 이 값을 넘을 수 없음.
  3. 몬스터 공격 (attacks):
    • 각 공격은 공격 시간피해량으로 구성된다.
    • 공격이 발생하는 순간 기술이 중단되고, 해당 초에는 체력이 회복되지 않으며, 공격 피해가 적용된다.

목표는 모든 공격이 끝난 후 캐릭터의 체력을 계산하는 것이다. 만약 캐릭터가 죽는다면, 즉 체력이 0 이하로 떨어진다면 -1을 반환한다.

시뮬레이션 절차는 다음과 같이 요약할 수 있다:

  1. 시간을 0부터 시작하여 최대 공격 시간까지 1초 단위로 증가시키면서 다음을 반복한다.
  2. 각 초마다 체크:
    • 현재 시간에 몬스터 공격이 있는지 확인한다.
    • 공격이 있으면 공격 피해를 적용하고, 연속 시전 시간을 초기화한다.
    • 공격이 없으면 체력을 x만큼 회복시킨다. 단, 회복 후 체력이 최대 체력을 초과하지 않도록 조정한다.
    • 연속 시전 시간이 t에 도달하면 추가 체력 y를 회복시킨다.
  3. 체력이 언제든지 0 이하로 떨어지면 즉시 -1을 반환한다.
  4. 모든 공격과 시간을 처리한 후 최종 체력을 반환한다.

 


def solution(bandage,health,attacks):
    answer=health
t,x,y=bandage

now_time=0
skill_stack=0
attack_idx=0
while True:
    now_time+=1

    if answer<=0:
        retrun -1

    if attack_idx == len(attacks):
        break

    #공격이 우선
    if now_time==attacks[attack_idx][0]:
        #attacks[attack_idx][1]은 현재 처리중인 공격의 피해량을 나타낸다
        # attacks 배열의 각 요소는 [공격 시간, 피해량]의 형태를 가지므로, 여기서 1 인덱스는 해당 공격의 피해량을 의미
        answer-=attacks[ataack_idx][1]
        
        #공격이 끝나면 다음 공격으로 넘어가기 위해 인덱스를 증가시킨다
        attack_idx +=1

        #공격을 받는순간 즉 now_time이 공격시간과 일치할때는 스킬을 사용할 수 없으므로 스킬스택을 0으로 초기화한다
        skill_stack=0
    
    #공격이 없는경우
    else:
        #붕대감기 기술이 연속적으로 사용된 시간을 카운트
        #공격을 받지 않는 경우 1초마다 증가
        skill_satck +=1

        #x는 붕대감기 기술에 의해 매 초 회복되는 체력의 양이다.
        #min(health, answer + x)은 체력이 최대 체력을 초과하지 않도록 보장한다
        answer=min(health,answer+x)

        #이 조건문은 변수가 붕대 감기 기술의 연속시전 시간인 t 에 도달했는지 검사 
        if skill_stack == t:
            
            #기술이 연속적으로 t초 동안 성공했다면 추가 회복 효과처리 적용
            #연속 사용시간을 충족하고 , 스택을 0으로 재설정한다 다시 시간을 계산하기 위함이다
            skill_stack=0
            answer == min(health,answr+y)

    return answer

3. [PCCE 기출문제] 9번 / 이웃한 칸

문제 설명

각 칸마다 색이 칠해진 2차원 격자 보드판이 있습니다. 그중 한 칸을 골랐을 때, 위, 아래, 왼쪽, 오른쪽 칸 중 같은 색깔로 칠해진 칸의 개수를 구하려고 합니다.

보드의 각 칸에 칠해진 색깔 이름이 담긴 이차원 문자열 리스트 board와 고른 칸의 위치를 나타내는 두 정수 h, w가 주어질 때 board[h][w]와 이웃한 칸들 중 같은 색으로 칠해져 있는 칸의 개수를 return 하도록 solution 함수를 완성해 주세요.

이웃한 칸들 중 몇 개의 칸이 같은 색으로 색칠되어 있는지 확인하는 과정은 다음과 같습니다.

1. 정수를 저장할 변수 n을 만들고 board의 길이를 저장합니다.
2. 같은 색으로 색칠된 칸의 개수를 저장할 변수 count를 만들고 0을 저장합니다.
3. h와 w의 변화량을 저장할 정수 리스트 dh, dw를 만들고 각각 [0, 1, -1, 0], [1, 0, 0, -1]을 저장합니다.
4. 반복문을 이용해 i 값을 0부터 3까지 1 씩 증가시키며 아래 작업을 반복합니다.
    4-1. 체크할 칸의 h, w 좌표를 나타내는 변수 h_check, w_check를 만들고 각각 h + dh[i], w + dw[i]를 저장합니다.
    4-2. h_check가 0 이상 n 미만이고 w_check가 0 이상 n 미만이라면 다음을 수행합니다.
        4-2-a. board[h][w]와 board[h_check][w_check]의 값이 동일하다면 count의 값을 1 증가시킵니다.
5. count의 값을 return합니다.
  • 위의 의사코드와 작동방식이 다른 코드를 작성해도 상관없습니다.

제한사항

  • 1 ≤ board의 길이 ≤ 7
    • board의 길이와 board[n]의 길이는 동일합니다.
  • 0 ≤ h, w < board의 길이
  • 1 ≤ board[h][w]의 길이 ≤ 10
    • board[h][w]는 영어 소문자로만 이루어져 있습니다.

코드 설계

1. 문제 이해 및 분석

  • 2차원 격자 보드판에서 주어진 위치 (h, w)의 색과 인접한 칸의 색을 비교하여 같은 색의 수를 찾아야 한다.
  • 인접한 칸은 상하좌우 네 방향이다.

2. 필요한 변수 정의

  • n: 격자의 크기 (길이)
  • count: 같은 색으로 칠해진 인접 칸의 수를 세기 위한 변수
  • dh와 dw: 각각 행과 열의 변화량을 나타내는 리스트. 이를 통해 상하좌우 칸을 참조할 수 있다.

3. 로직 설계

  1. board의 길이를 n으로 저장한다.
  2. count를 0으로 초기화한다.
  3. dh에 [0, 1, -1, 0]을, dw에 [1, 0, 0, -1]을 저장하여 상, 하, 좌, 우 방향을 나타낸다.
  4. 0부터 3까지 반복하여 각 방향을 검사한다:
    • h_check와 w_check를 계산하여 인접한 칸의 위치를 정한다.
    • 계산된 위치가 격자의 범위 내에 있는지 확인한다.
    • 범위 내에 있고, 인접 칸의 색이 선택된 칸의 색과 같은지 비교한다.
    • 같다면 count를 증가시킨다.
  5. 모든 방향을 검사한 후, count를 반환한다.

4. 예외 처리

  • 격자의 범위를 벗어나는 인덱스에 대한 접근을 막기 위해 범위 확인이 필요하다.

5. 코드 구현

  • 위 설계를 바탕으로 코드를 작성한다.
def solution(board,h,w):
    n=len(board)
    count=0
    dh=[0,1,-1,0]
    dw=[1,0,0,-1]

    for i in range(4):
        #체크할 행 위치
        h_check=h+dh[i]
        #새로운 열 위치
        w_check=w+dw[i]

        #인덱스 범위 검사
        if 0<=h_check<n and 0 <=w_check<n:
            #같은색인지 확인
            if board[h][w]==board[h_check][w_check]:
                count+=1

    return count

#예시 입력에 대한 함수 실행
board1=[["blue","red","orange","red"],
        ["red","red","blue","orange"],
        ["blue","orange","red","red"],
        ["orange","orange","red","blue"]]

print(solution(board1,1,1))

board2=[
    ["yellow","green","blue"],
    ["blue","green","yellow"],
    ["yellow","blue","blue"]
]

print(solution(board2,0,1))

 


4. [PCCE 기출문제] 10번 / 데이터 분석

문제 설명

AI 엔지니어인 현식이는 데이터를 분석하는 작업을 진행하고 있습니다. 데이터는 ["코드 번호(code)", "제조일(date)", "최대 수량(maximum)", "현재 수량(remain)"]으로 구성되어 있으며 현식이는 이 데이터들 중 조건을 만족하는 데이터만 뽑아서 정렬하려 합니다.

예를 들어 다음과 같이 데이터가 주어진다면

data = [[1, 20300104, 100, 80], [2, 20300804, 847, 37], [3, 20300401, 10, 8]]

이 데이터는 다음 표처럼 나타낼 수 있습니다.

codedatemaximumremain
1 20300104 100 80
2 20300804 847 37
3 20300401 10 8

주어진 데이터 중 "제조일이 20300501 이전인 물건들을 현재 수량이 적은 순서"로 정렬해야 한다면 조건에 맞게 가공된 데이터는 다음과 같습니다.

data = [[3,20300401,10,8],[1,20300104,100,80]]

정렬한 데이터들이 담긴 이차원 정수 리스트 data와 어떤 정보를 기준으로 데이터를 뽑아낼지를 의미하는 문자열 ext, 뽑아낼 정보의 기준값을 나타내는 정수 val_ext, 정보를 정렬할 기준이 되는 문자열 sort_by가 주어집니다.

data에서 ext 값이 val_ext보다 작은 데이터만 뽑은 후, sort_by에 해당하는 값을 기준으로 오름차순으로 정렬하여 return 하도록 solution 함수를 완성해 주세요. 단, 조건을 만족하는 데이터는 항상 한 개 이상 존재합니다.


제한사항

  • 1 ≤ data의 길이 ≤ 500
    • data[i]의 원소는 [코드 번호(code), 제조일(date), 최대 수량(maximum), 현재 수량(remain)] 형태입니다.
    • 1 ≤ 코드 번호≤ 100,000
    • 20000101 ≤ 제조일≤ 29991231
    • data[i][1]은 yyyymmdd 형태의 값을 가지며, 올바른 날짜만 주어집니다. (yyyy : 연도, mm : 월, dd : 일)
    • 1 ≤ 최대 수량≤ 10,000
    • 1 ≤ 현재 수량≤ 최대 수량
  • ext와 sort_by의 값은 다음 중 한 가지를 가집니다.
    • "code", "date", "maximum", "remain"
    • 순서대로 코드 번호, 제조일, 최대 수량, 현재 수량을 의미합니다.
  • val_ext는 ext에 따라 올바른 범위의 숫자로 주어집니다.
  • 정렬 기준에 해당하는 값이 서로 같은 경우는 없습니다.
def solution(data, ext, val_ext, sort_by):
    # 칼럼 이름을 인덱스로 매핑
    index_map = {"code": 0, "date": 1, "maximum": 2, "remain": 3}
    
    # ext와 sort_by에 해당하는 인덱스 가져오기
    ext_index = index_map[ext]
    sort_index = index_map[sort_by]
    
    # val_ext보다 작은 값을 필터링
    filtered_data = [row for row in data if row[ext_index] < val_ext]
    
    # sort_by 칼럼을 기준으로 오름차순 정렬
    sorted_data = sorted(filtered_data, key=lambda x: x[sort_index])
    
    return sorted_data