[python] 멀티탭 스케줄링 백준 1700

Designated Hitter Jack·2023년 8월 30일

백준 - 파이썬

목록 보기
5/26
post-thumbnail

문제

기숙사에서 살고 있는 준규는 한 개의 멀티탭을 이용하고 있다. 준규는 키보드, 헤어드라이기, 핸드폰 충전기, 디지털 카메라 충전기 등 여러 개의 전기용품을 사용하면서 어쩔 수 없이 각종 전기용품의 플러그를 뺐다 꽂았다 하는 불편함을 겪고 있다. 그래서 준규는 자신의 생활 패턴을 분석하여, 자기가 사용하고 있는 전기용품의 사용순서를 알아내었고, 이를 기반으로 플러그를 빼는 횟수를 최소화하는 방법을 고안하여 보다 쾌적한 생활환경을 만들려고 한다.

예를 들어 3 구(구멍이 세 개 달린) 멀티탭을 쓸 때, 전기용품의 사용 순서가 아래와 같이 주어진다면,

키보드
헤어드라이기
핸드폰 충전기
디지털 카메라 충전기
키보드
헤어드라이기
키보드, 헤어드라이기, 핸드폰 충전기의 플러그를 순서대로 멀티탭에 꽂은 다음 디지털 카메라 충전기 플러그를 꽂기 전에 핸드폰충전기 플러그를 빼는 것이 최적일 것이므로 플러그는 한 번만 빼면 된다.

입력

첫 줄에는 멀티탭 구멍의 개수 N (1 ≤ N ≤ 100)과 전기 용품의 총 사용횟수 K (1 ≤ K ≤ 100)가 정수로 주어진다. 두 번째 줄에는 전기용품의 이름이 K 이하의 자연수로 사용 순서대로 주어진다. 각 줄의 모든 정수 사이는 공백문자로 구분되어 있다.

출력

하나씩 플러그를 빼는 최소의 횟수를 출력하시오.

예제 입력 1

2 7
2 3 2 3 1 2 7

예제 출력 1

2

나의 풀이

import sys
input = sys.stdin.readline

N, K = map(int, input().split())
if N >= K: #멀티탭이 전자기기보다 커서 콘센트를 뺄 필요가 없을 때
    print(0)
    exit() #이런 메소드가 있다는 걸 이 문제 풀면서 처음 알았다.

elec_list = list(map(int, input().split()))

#한 멀티탭 안에 전자기기가 중복해서 들어오는 경우를 제거하기 위해서 set으로 선언
plug = set() 
cnt = 0
#멀티탭에 빈 칸이 없을 경우, 가장 나중에 출현하는 플러그를 뽑아야 함
def find_latest(idx): 
    result = 0
    max_idx = -1
    for num in plug:
        try:
            num_idx = elec_list[idx:].index(num)
        except:
            num_idx = K
        if max_idx < num_idx:
            result, max_idx = num, num_idx
    return result

for idx, num in enumerate(elec_list):
    plug.add(num)
    if len(plug) > N:
        cnt += 1
        latest_used = find_latest(idx)
        plug.discard(latest_used)

print(cnt)

풀이 과정

사실 처음부터 이렇게 생각하진 못했다. 처음엔 멀티탭의 길이만큼의 리스트를 만들고, 한번에 그 리스트에서 사용할 수 있는 전자기기를 저장한 후, 다음 리스트에서 이전 리스트에 없었던 전자기기가 있다면 count를 하나 올리는 식으로 알고리즘을 생각했었다.

예제는 이런식으로 알고리즘을 짰어도 풀렸지만 중요한 반례가 두 가지 있었다.
같은 기기가 사용 순서에서 연속해서 나오는 경우. [1,2],[2,3],[4,3]
멀티탭 리스트 안에 똑같은 기기가 존재하는 경우. [1,2,1]
밑의 코드는 앞에서 언급한 두 가지 반례를 당장 해결하기 위해서 잘못된 로직을 바탕으로 짠 누더기 코드다.

import sys
input = sys.stdin.readline

N, K = map(int, input().split()) #N: 멀티탭 구멍의 개수, K: 전기용품의 총 사용횟수

elec_list = list(map(int, input().split()))
print(elec_list)
#[2,3,2,3,1,2,7]
# plug_list = [elec_list[i:i + N] for i in range(0, len(elec_list), N)]
no_dup = [elec_list[0]]
for i in range(1, len(elec_list)): #연속된 전자기기가 같은 경우 제거
    if elec_list[i - 1] == elec_list[i]:
        continue
    else:
        no_dup.append(elec_list[i])
print(no_dup)

plug_list = []
multitap = set() #멀티탭을 set으로 선언해 멀티탭 안에 [1,2,1] 같은 똑같은 기기가 다른 곳에 꼽히는 경우를 삭제함
for i in range(len(no_dup)):
    multitap.add(no_dup[i])
    if len(multitap) == N or i == len(no_dup) - 1:
        multitap_done = list(multitap) #중복을 제거한 멀티탭의 경우를 계산하기 쉽게 list로 변환

        plug_list.append(multitap_done)
        multitap.clear()
print(plug_list)
#[[2,3],[2,3],[1,2],[7]]
count = 0

for i in range(1, len(plug_list)):
    for j in range(len(plug_list[i])):
        if plug_list[i][j] not in plug_list[i - 1]:
            count += 1

print(count)

이렇게 코드를 짜니 정답이 나오던 입력이 오답이 나오는 등 출력도 엉망이 되고 내 머릿속도 엉망이 되어서 결국 로직을 어떻게 짜야 정답이 나오는지 다른 글을 참고했다. 올바른 로직은 '되도록 나중에 등장하는 전자제품을 뽑는 것' 이었다.

profile
Fear always springs from ignorance.

0개의 댓글