리스트의 매 원소마다 해당 원소보다 작은 고유한 원소들이 몇 개인지 세서 계산하는 방식을 쓰면 시간 초과가 나는 문제였다. 

 

prefix sum 방식과 유사하게, 해당 원소보다 작은 개수인 고유한 원소의 개수가 몇 개인지를 미리 unique_list 배열에 저장해 두고, 정답 배열에 이 값을 추가하는 방식으로 시간초과를 막을 수 있었다. 

import sys
from collections import defaultdict
input = sys.stdin.readline

N = int(input())
arr = list(map(int, input().split()))

unique_list = defaultdict(lambda: 0)
for index, a in enumerate(sorted(list(set(arr)))):
    unique_list[a] = index

ans = []
for a in arr:
    ans.append(str(unique_list[a]))
    
print(" ".join(ans))

 

 

bfs 또는 dfs 방식으로 그래프를 탐색하면 된다. 

만약 모든 노드가 이어져 있다면, 즉 연결 요소의 개수가 1개라면 그래프 탐색으로 모든 노드를 방문할 수 있을 것이다. 

그게 아니라면 연결 요소의 개수만큼 그래프를 탐색해야 모든 노드를 방문할 수 있을 것이다. 

 

u, v 노드 사이의 간선을 저장하는 방법은 2차원 배열도 있고 여러 가지가 있으나, 여기서는 collections.defaultdict()을 사용해서 딕셔너리에 노드 값 하나와 노드와 이어진 다른 노드들의 리스트를 키-값으로 맵핑하였다. 

 

또한 여기서는 그래프의 모든 노드를 순회하면서, 방문하지 않은 노드가 있을 경우 그 노드를 시작점으로 bfs 탐색을 진행하였다. 

한 번 탐색할 때마다 연결 요소의 개수도 증가하므로 ans 변수의 값을 1씩 증가시켰다. 

 

import sys
from collections import defaultdict, deque

input = sys.stdin.readline
N, M = map(int, input().split())
graph = defaultdict(list)
visited = [False] * (N+1)

for _ in range(M):
    u, v = map(int, input().split())
    graph[u].append(v)
    graph[v].append(u)
    
def bfs(start):
    Q = deque([start])
    visited[start] = True
    while Q:
        node = Q.popleft()
        for n in graph[node]:
            if not visited[n]:
                visited[n] = True
                Q.append(n)

ans = 0
for i in range(1, N+1):
    if not visited[i]:
        bfs(i)
        ans += 1

print(ans)

 

 

그래프를 탐색하되, bfs나 dfs처럼 연쇄적으로 탐색하지 말고 한 번에 근처에 있는 주변 노드들만 탐색한다. 

연쇄적으로 한 번에 탐색하면 안 되는 이유는 총 탐색하는데 며칠이 걸리는지를 알아내야 하기 때문이다. 

 

따라서 데크(deque)를 선언하고 인접한 노드를 데크에 추가하는 bfs의 방식을 활용하되, 데크에 새로 인접한 노드들을 추가하지는 않았다. 

즉 한 번 bfs()를 실행할 때마다 익은 토마토 주변의 토마토만 익게 된다. bfs() 함수에서는 배열의 값이 0인 칸들만 1로 바꿔주는 것으로 이를 대신하였다. 

배열의 값이 -1인 경우는 토마토가 없는 것이므로 인접한 토마토가 익는 효과가 나타나지 않는다. 그러므로 배열의 값이 꼭 0일 때만 1로 만들어 주어야 한다. 

 

ripe_list라는 변수를 선언해서 맨 처음에 익어 있는 토마토를 해당 변수에 추가한다. 

이후에는 bfs()를 실행하면서 맨 처음에 익어 있는 토마토에 의해 새로 익게 된 토마토만 ripe_list 변수에 추가해 준다. 

이런 방식으로 while 문을 실행하면서, 새로 익게 되는 토마토가 없는 경우 탐색을 종료한다. 

while 문에서는 day 값을 1씩 늘려가면서 탐색한다. 이때 day 값을 0으로 하면 0번째 값을 탐색할 때에도 day 값이 1씩 추가되기 때문에, day 값을 -1로 두고 시작한다. 

 

새로 익게 되는 토마토가 없는 경우, 즉 ripe_list의 값이 없는 경우 while 문을 빠져나오고 난 뒤에는 전체 배열을 순회하면서 값이 0인 칸을 탐색한다. 

만약 값이 0인 칸이 있다면 익지 않은 토마토가 있다는 뜻이므로 토마토가 모두 익지 못한 것이다. 이 경우에는 -1을 출력하고, 나머지 경우에는 while문으로 값을 증가시킨 day 값을 출력한다. 

 

import sys
from collections import deque
input = sys.stdin.readline

dx = [0, 0, -1, 1]
dy = [-1, 1, 0, 0]

def bfs(ripe):
    Q = deque(ripe)
    new_ripe = []
    while Q:
        x, y = Q.popleft()
        for k in range(4):
            nx = x + dx[k]
            ny = y + dy[k]
            if nx >= N or nx < 0 or ny >= M or ny < 0:
                continue
            if arr[nx][ny] == 0:
                arr[nx][ny] = 1
                new_ripe.append([nx, ny])
    return new_ripe

M, N = map(int, input().split())
arr = []
for _ in range(N):
    arr.append(list(map(int, input().split())))
    
ripe_list = []
for i in range(N):
    for j in range(M):
        if arr[i][j] == 1:
            ripe_list.append([i, j])

day = -1
while ripe_list:
    ripe_list = bfs(ripe_list)
    day += 1

ripe = True
for i in range(N):
    for j in range(M):
        if arr[i][j] == 0:
            ripe = False
            break
if not ripe:
    print(-1)
else:
    print(day)

 

 

 

문제의 원래 의도는 최소 힙(min heap)을 구현하는 것이나, 문제를 푼 방법을 설명하는 코테 등이 아니라면 파이썬에 구현되어 있는 heapq 라이브러리를 쓰면 쉽게 풀 수 있다. 

 

heap으로 사용하기 위해서 heap이라는 리스트 변수를 선언했다. 

heap에 원소를 넣기 위해서는 heapq.heappush(heap변수, 추가할 원소) 를 사용한다. 

반대로 heap에서 가장 작은 원소를 빼기 위해서는 heapq.heappop(heap변수)를 사용한다. 

 

heapq 라이브러리는 최소 힙으로 구현되어 있지만, 최대 힙으로도 사용할 수 있다. 

앞서 heappush 연산을 할 때 추가할 원소의 값을 음수로 넣어주면 양수로는 가장 큰 원소가 가장 작은 원소로 간주되어 맨 마지막에 나오게 된다. 

 

import heapq
import sys
input = sys.stdin.readline

N = int(input())
heap = []
for _ in range(N):
    command = int(input())
    if command == 0:
        if heap:
            elem = heapq.heappop(heap)
            print(elem)
        else:
            print(0)
    else:
        heapq.heappush(heap, command)

 

 

 

몇 번째로 출력되는지 알아야 할 원소의 초기 인덱스인 M을 사용해서 해당 원소가 현재 몇 번째 인덱스에 있는지를 관찰하였다.

M의 경우 매 시행마다 앞으로 한 칸씩 당겨지므로 1씩 값을 제거하면서 그 값을 배열의 길이로 나눈 나머지를 사용하였다. 그래야 M의 값이 원소의 인덱스 범위를 벗어나지 않고 정확히 원소의 위치를 가리킬 수 있다. 

 

또한 cnt 변수를 사용해서 원소를 Q에서 제거하고 출력할 때마다 cnt 값을 1씩 증가시켰다. 

 

매 시행마다 Q(데크)에서 맨 앞의 원소를 뺀 뒤, 해당 원소의 뒤에 있는 원소들 중에서 해당 원소보다 큰 원소가 없으면 해당 원소를 제거했다.

만약 해당 원소보다 큰 원소가 뒤에 있었다면, popleft()로 제거했던 맨 앞의 원소를 다시 append()를 사용해서 배열의 맨 뒤에 넣어주었다. 

 

import sys
from collections import deque
input = sys.stdin.readline

T = int(input())

for _ in range(T):

    N, M = map(int, input().split())
    Q = deque(list(map(int, input().split())))
    
    if N == 1:
        print(1)
    else:
        cnt = 0
        
        while Q:
            elem = Q.popleft()
            
            if Q and max(Q) > elem:
                Q.append(elem)
            else:
                cnt += 1
                if M == 0:
                    break
            M = (M-1) % len(Q)
                    
        print(cnt)

 

 

 

상태 변화를 체크할 변수들을 몇 개 선언하고, 실제로 스택에 수를 넣었다가 빼는 작업을 하면 되는 문제다. 

 

1. queue 변수에는 스택에서 출력해야 하는 변수들을 차례대로 입력받아 넣었다. 

2. stack 변수는 실제 스택 역할을 하는 변수이다. 

3. maxNum 변수는 스택에 들어갔었던 가장 큰 숫자 값을 저장하는 변수이다. 스택에 push를 할 때는 모든 원소를 오름차순으로 넣어야 하기 때문에 이 값이 필요했다.

4. ops 변수는 스택에서 어떤 연산이 수행되었는지 그 값들을 모으기 위한 리스트 변수이다. 

 

queue 변수는 데크 자료형으로 선언해서, 맨 앞에서 원소를 뺄 때도 O(1) 시간에 제거할 수 있도록 했다. 

 

만약 현재 stack에서 출력되어야 하는 원소(queue의 맨 앞에서 빼낸 원소)가 stack의 maxNum 값보다 크다면, stack에 수를 더 넣어야 한다는 뜻이므로 for문을 사용해서 stack에 원소를 1씩 증가시키며 추가해 주었다. 또한 stack에 원소를 추가할 때마다 ops 변수에도 "+" 값을 추가해 주었다. 

 

만약 그렇지 않다면, queue에서 빼낸 원소와 stack의 맨 뒤의 값이 일치하는지를 확인했다. 두 값이 일치한다면 해당 값을 stack에서 빼내서 출력해야 하므로 stack에서 pop()으로 원소를 빼내고, ops 변수에도 "-" 값을 추가해 주었다. 

 

또한 queue와 stack에는 모두 1부터 N까지의 원소가 들어갔다 나와야 하기 때문에, 정상적으로 수행되었다면 queue가 빌 때 stack도 비어야 한다. 

만일 그렇지 않다면 중간에 queue에서 빼낸 값과 stack의 맨 뒤의 값이 일치하지 않아서 stack에서 원소를 빼낼 수 없었다는 뜻이므로, while 문을 다 돌려보면 queue는 비는데 stack에는 원소가 남게 된다. 

 

따라서 이 경우에만 에러를 출력한다. 

 

import sys
from collections import deque
input = sys.stdin.readline

N = int(input())
queue = deque([])

for _ in range(N):
    queue.append(int(input()))
    
stack = []
ops = []
maxNum = 0

while queue:
    
    elem = queue.popleft()
    
    if maxNum < elem:
        for i in range(maxNum+1, elem+1):
            stack.append(i)
            ops.append("+")
        maxNum = max(max(stack), maxNum)
    
    if stack and elem == stack[-1]:
        stack.pop()
        ops.append("-")
        
if stack:
    print("NO")
else:
    for op in ops:
        print(op)

 

 

 

파이썬의 딕셔너리 자료구조형은 해시 테이블(hash table)으로 구현되어 있는데, hash table의 경우 일반적인 리스트보다 내부 원소에 더 빠르게 접근할 수 있다. 

 

따라서 never_listen 변수를 리스트로 선언하지 않고 딕셔너리로 선언해서 M번의 루프를 돌 때 해당 이름이 never_listen 내부에 있는 값인지 더 빨리 확인할 수 있도록 하였다.

 

import sys
from collections import defaultdict
input = sys.stdin.readline

N, M = map(int, input().split())
never_listen = defaultdict(lambda: False)
never_listen_and_see = []

for _ in range(N):
    name = input().strip()
    never_listen[name] = True

for _ in range(M):
    name = input().strip()
    if never_listen[name]:
        never_listen_and_see.append(name)
        
never_listen_and_see.sort()

print(len(never_listen_and_see))
for nlas in never_listen_and_see:
    print(nlas)

 

 

 

import sys
input = sys.stdin.readline

N = int(input())
S = set()

for _ in range(N):
    command = input().strip()
    
    if command == "all":
        S = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20}
    elif command == "empty":
        S = set()
    else:
        cmd, elem = command.split()
        elem = int(elem)
        
        if cmd == "add":
            S.add(elem)
        elif cmd == "remove":
            if elem in S:
                S.remove(elem)
        elif cmd == "check":
            if elem in S:
                print(1)
            else:
                print(0)
        else:
            if elem in S:
                S.remove(elem)
            else:
                S.add(elem)

 

 

+ Recent posts