[알고리즘] Programmers 롤케이크 자르기 #Python

김상현·2022년 12월 18일
0

알고리즘

목록 보기
247/301
post-thumbnail

[Programmers] 롤케이크 자르기 바로가기

📍 문제 설명

철수는 롤케이크를 두 조각으로 잘라서 동생과 한 조각씩 나눠 먹으려고 합니다. 이 롤케이크에는 여러가지 토핑들이 일렬로 올려져 있습니다. 철수와 동생은 롤케이크를 공평하게 나눠먹으려 하는데, 그들은 롤케이크의 크기보다 롤케이크 위에 올려진 토핑들의 종류에 더 관심이 많습니다. 그래서 잘린 조각들의 크기와 올려진 토핑의 개수에 상관없이 각 조각에 동일한 가짓수의 토핑이 올라가면 공평하게 롤케이크가 나누어진 것으로 생각합니다.

예를 들어, 롤케이크에 4가지 종류의 토핑이 올려져 있다고 합시다. 토핑들을 1, 2, 3, 4와 같이 번호로 표시했을 때, 케이크 위에 토핑들이 [1, 2, 1, 3, 1, 4, 1, 2] 순서로 올려져 있습니다. 만약 세 번째 토핑(1)과 네 번째 토핑(3) 사이를 자르면 롤케이크의 토핑은 [1, 2, 1], [3, 1, 4, 1, 2]로 나뉘게 됩니다. 철수가 [1, 2, 1]이 놓인 조각을, 동생이 [3, 1, 4, 1, 2]가 놓인 조각을 먹게 되면 철수는 두 가지 토핑(1, 2)을 맛볼 수 있지만, 동생은 네 가지 토핑(1, 2, 3, 4)을 맛볼 수 있으므로, 이는 공평하게 나누어진 것이 아닙니다. 만약 롤케이크의 네 번째 토핑(3)과 다섯 번째 토핑(1) 사이를 자르면 [1, 2, 1, 3], [1, 4, 1, 2]로 나뉘게 됩니다. 이 경우 철수는 세 가지 토핑(1, 2, 3)을, 동생도 세 가지 토핑(1, 2, 4)을 맛볼 수 있으므로, 이는 공평하게 나누어진 것입니다. 공평하게 롤케이크를 자르는 방법은 여러가지 일 수 있습니다. 위의 롤케이크를 [1, 2, 1, 3, 1], [4, 1, 2]으로 잘라도 공평하게 나뉩니다. 어떤 경우에는 롤케이크를 공평하게 나누지 못할 수도 있습니다.

롤케이크에 올려진 토핑들의 번호를 저장한 정수 배열 topping이 매개변수로 주어질 때, 롤케이크를 공평하게 자르는 방법의 수를 return 하도록 solution 함수를 완성해주세요.


📍 제한사항

  • 1 ≤ topping의 길이 ≤ 1,000,000
    • 1 ≤ topping의 원소 ≤ 10,000

📍 입출력 예

toppingresult
[1, 2, 1, 3, 1, 4, 1, 2]2
[1, 2, 3, 1, 4]0

📍 입출력 예 설명

입출력 예 #1

  • 롤케이크를 [1, 2, 1, 3], [1, 4, 1, 2] 또는 [1, 2, 1, 3, 1], [4, 1, 2]와 같이 자르면 철수와 동생은 각각 세 가지 토핑을 맛볼 수 있습니다. 이 경우 공평하게 롤케이크를 나누는 방법은 위의 두 가지만 존재합니다.

입출력 예 #2

  • 롤케이크를 공평하게 나눌 수 없습니다.

📍 풀이

💡 고찰

  • 모든 경우 중 조건에 부합하는 경우의 수를 구하는 문제이다.
  • 문제의 제한사항을 보면 topping의 길이가 1,000,000인 것을 알 수 있다.
  • 모든 경우를 확인할 때마다 많은 시간 복잡도가 발생하면 시간 초과 문제가 발생할 수 있다.
  • 따라서 모든 경우를 조건에 맞추어 검사하는 연산의 시간 복잡도가 최소가 될 수 있도록 자료값을 초기화하였다.

📌 문제 풀이

✏️ [1] 토핑의 종류, 토핑의 개수 초기화

A, B = set(), Counter(toppings)
a, b = 0, len(B)
  • A 는 철수가 현재 가지고 있는 롤케이크에 토핑의 종류를 저장할 집합(set) 자료구조이다.
    • 철수 롤케이크 토핑의 종류(A)는 원소 제거 없이 항상 원소 추가 작업만 수행된다.
    • 따라서 현재 가지고 있는 토핑의 종류만을 관리하는 집합(set) 자료구조를 사용한다.
  • B 는 동생이 가지고 있는 롤케이크에 토핑의 종류와 개수를 저장한 딕셔너리(dictionary) 자료구조이다.
    • 동생 롤케이크 토핑의 종류(B)는 항상 원소 제거 작업만 수행된다.
    • 따라서 토핑의 종류와 각 종류 별 개수에 대한 정보를 관리할 수 있는 딕셔너리(dictionary) 자료구조를 사용한다.
  • a 는 철수의 롤케이크에 존재하는 토핑의 개수이다.
  • b 는 동생의 롤케이크에 존재하는 토핑의 개수이다.

✏️ [2] 전수 조사

for topping in toppings:

    # 동생 롤케이크(B)의 토핑(topping)의 개수 1 감소
    B[topping] -= 1

    # 동생 롤케이크(B)에 토핑(topping)이 더이상 존재하지 않을 경우
    if B[topping] == 0:
        # 동생 롤케이크에 존재하는 토핑 개수(b) 1 감소
        b -= 1 

    # 철수 롤케이크(A)에 토핑(topping)이 존재하지 않을 경우
    if topping not in A:
        # 철수 롤케이크(A)에 토핑(topping) 추가
        A.add(topping)
        # 철수 롤케이크에 존재하는 토핑 개수(a) 1 증가
        a += 1

    # 철수와 동생의 토핑의 개수가 같을 경우
    if a == b: answer += 1
  • 모든 롤케이크를 동생에게 할당한 상태에서 왼쪽을 기준으로 토핑에 해당하는 크기만큼 잘라서 철수에게 할당하는 작업을 수행한다.
  • 모든 작업은 O(1) 시간 안에 처리할 수 있다.

✍ 코드

from collections import Counter

def solution(toppings):
    answer = 0
    A, B = set(), Counter(toppings) # 토핑의 종류
    a, b = 0, len(B) # 토핑의 개수
    for topping in toppings:
        
        # 동생 롤케이크(B)의 토핑(topping)의 개수 1 감소
        B[topping] -= 1
        
        # 동생 롤케이크(B)에 토핑(topping)이 더이상 존재하지 않을 경우
        if B[topping] == 0:
            # 동생 롤케이크에 존재하는 토핑 개수(b) 1 감소
            b -= 1 
        
        # 철수 롤케이크(A)에 토핑(topping)이 존재하지 않을 경우
        if topping not in A:
            # 철수 롤케이크(A)에 토핑(topping) 추가
            A.add(topping)
            # 철수 롤케이크에 존재하는 토핑 개수(a) 1 증가
            a += 1
        
        # 철수와 동생의 토핑의 개수가 같을 경우
        if a == b: answer += 1
        
        # 이후의 연산은 무의미
        if a > b: break
        
    return answer
profile
목적 있는 글쓰기

0개의 댓글