99클럽 코테 스터디 20일차 TIL (체육복) - 프로그래머스

말하는 감자·2024년 8월 10일
1

99클럽 3기

목록 보기
20/42
post-thumbnail
post-custom-banner

1. 오늘의 학습 키워드

  • 그리디 알고리즘
  • 탐욕법

2. 문제: 체육복

문제 설명

점심시간에 도둑이 들어, 일부 학생이 체육복을 도난당했습니다. 다행히 여벌 체육복이 있는 학생이 이들에게 체육복을 빌려주려 합니다. 학생들의 번호는 체격 순으로 매겨져 있어, 바로 앞번호의 학생이나 바로 뒷번호의 학생에게만 체육복을 빌려줄 수 있습니다. 예를 들어, 4번 학생은 3번 학생이나 5번 학생에게만 체육복을 빌려줄 수 있습니다. 체육복이 없으면 수업을 들을 수 없기 때문에 체육복을 적절히 빌려 최대한 많은 학생이 체육수업을 들어야 합니다.

전체 학생의 수 n, 체육복을 도난당한 학생들의 번호가 담긴 배열 lost, 여벌의 체육복을 가져온 학생들의 번호가 담긴 배열 reserve가 매개변수로 주어질 때, 체육수업을 들을 수 있는 학생의 최댓값을 return 하도록 solution 함수를 작성해주세요.

제한사항

  • 전체 학생의 수는 2명 이상 30명 이하입니다.
  • 체육복을 도난당한 학생의 수는 1명 이상 n명 이하이고 중복되는 번호는 없습니다.
  • 여벌의 체육복을 가져온 학생의 수는 1명 이상 n명 이하이고 중복되는 번호는 없습니다.
  • 여벌 체육복이 있는 학생만 다른 학생에게 체육복을 빌려줄 수 있습니다.
  • 여벌 체육복을 가져온 학생이 체육복을 도난당했을 수 있습니다. 이때 이 학생은 체육복을 하나만 도난당했다고 가정하며, 남은 체육복이 하나이기에 다른 학생에게는 체육복을 빌려줄 수 없습니다.

입출력 예

nlostreservereturn
5[2, 4][1, 3, 5]5
5[2, 4][3]4
3[3][1]2

입출력 예 설명

예제 #1

1번 학생이 2번 학생에게 체육복을 빌려주고, 3번 학생이나 5번 학생이 4번 학생에게 체육복을 빌려주면 학생 5명이 체육수업을 들을 수 있습니다.

예제 #2

3번 학생이 2번 학생이나 4번 학생에게 체육복을 빌려주면 학생 4명이 체육수업을 들을 수 있습니다.


3. 나의 풀이

접근 방법

이 문제는 그리디 알고리즘 카테고리에 속해있는 문제이다. 즉, 선택의 순간에서의 최적의 상황이 전체적으로도 최적이 적용되도록 출제된 문제라고 볼 수 있다.

문제를 살펴보면 인자로는 전체 학생의 수 n, 체육복을 도난당한 학생들 lost, 여벌의 체육복을 가져온 학생들 reserve가 있다. 이 상황에서 체육수업을 들을 수 있는 학생의 최댓값을 구하고자 하는 것이다.

그리디 문제이니 reserve의 한 학생이 lost학생에게 여벌의 체육복을 나눠주는 최적의 방법이 체육 수업을 들을 수 있는 학생의 최댓값을 구하는데 적합하다는것을 보장할 수 있다.

문제의 특징 중 하나는 학생들의 번호가 있는데, 빌려주는 사람 기준 앞 번호나 뒷 번호에 학생들에게 여벌의 체육복을 빌려줄 수 있다고 한다.

또한, lost학생, reserve학생 각각 모두 중복되는 번호는 없다.

그리고, 이게 가장 중요한 정보인 것 같은데,

“여벌 체육복을 가져온 학생이 체육복을 도난당했을 수 있습니다. 이때 이 학생은 체육복을 하나만 도난당했다고 가정하며, 남은 체육복이 하나이기에 다른 학생에게는 체육복을 빌려줄 수 없습니다.”

위 내용을 예시로 보자면 다음과 같다.

lost = [1, 2, 3], reserve = [3,4,5] 와 같이 나온다면 3번의 학생은 여벌 체육복을 가져왔지만 도난당해 빌려줄 수 없는 상황이 되는것이다.

그렇다면 reserve에서 3번을 제외하는 것이 맞다. 또한 lost에서도 3번을 제외해야 한다. 왜냐하면 잃어버렸지만, 3번은 여벌의 체육복을 잃어버린것이지 자기것은 가지고 있기 때문이다.

그러면 이 제한 조건을 만족시키기 위해서는 reserve, lost 리스트에서 동일한 요소를 제거해야한다.

# 마지막 제한 사항: 여벌 체육복을 가져온 학생 (reserve)이 체육복을 도난당했을 수 있다.
reserve_set = set(reserve) - set(lost)
lost_set = set(lost) - set(reserve)

그 다음, 이제 확인해야 할 방법은 reserve_set으로부터 반복문을 돌리면서 해당 요소의 앞번호, 뒷번호에 해당하는 번호가 lost_set에 있다면, lost_set에서 제거하는 방식을 구현해야한다.

여기서, 또 하나 짚어야 할 점은 여벌의 체육복을 빌려주는 학생 기준, 왼쪽이 최적이냐 오른쪽이 최적이냐라는 문제가 있다.

만약, n = 5, lost = [2, 4], reserve = [3, 5]인 경우를 살펴보자.

3번학생은 2번에게도 줄 수 있고, 4번에게도 줄 수 있다. 그리고 5번학생은 4번에게 줄 수 있다. 그러면 4번은 3번에게도, 5번에게도 체육복을 받을 수 있다. 만약 3번 학생이 4번에게 줘버리면 2번은 체육복을 받을 수 없는 상황이 되기 때문에, 체육복을 양 옆 학생에게 빌려줄 수 있다면 왼쪽 요소부터 탐색해야 함을 알 수 있다.

코드 구현은 아래와 같다.

for i in reserve_set:
    if i-1 in lost_set:
        lost_set.remove(i-1)
     elif i+1 in lost_set:
        lost_set.remove(i+1)

최종적으로, 체육수업을 들을 수 있는 학생의 최댓값은 전체 학생수 n에서 lost_set에 속해있는 학생을 제외하면 된다.

최종적인 코드는 아래와 같다.

def solution(n, lost, reserve):

    
    # 마지막 제한 사항: 여벌 체육복을 가져온 학생 (reserve)이 체육복을 도난당했을 수 있다.
    reserve_set = set(reserve) - set(lost)
    lost_set = set(lost) - set(reserve)
    
    for i in reserve_set:
        if i-1 in lost_set:
            lost_set.remove(i-1)
        elif i+1 in lost_set:
            lost_set.remove(i+1)
    
    return n - len(lost_set)

Test 결과

최종 결과


4. 결론

이 문제는 그리디 알고리즘을 사용해 reserve의 요소마다 local optimum인 왼쪽 요소부터 탐색하여 전체 최적의 해를 구하는 문제였다.

개인적으로 그리디 알고리즘을 1문제 풀어봤고, 어제 TIL에서도 풀고, 오늘까지 푼거면 3문제를 푼 것인데 뭔가 그리디 알고리즘의 유형이 있는것 같다.

  1. 주어진 인자들을 재배열
  2. 반복문에서 조건에 맞는것을 찾기 (여기서 조건이 local optimal한 것)

조만간에 그리디 알고리즘 개념을 정리하고, 여러 문제를 익혀봐야겠다.

읽어주셔서 감사합니다.

profile
할 수 있다
post-custom-banner

0개의 댓글