폴더 분리

This commit is contained in:
2026-02-23 11:14:50 +09:00
parent 4aebd20cf5
commit 0e8e68d12f
45 changed files with 0 additions and 0 deletions

View File

@@ -0,0 +1,46 @@
# 줄세우기
import sys
input = sys.stdin.readline
def test(students):
line = [float('inf')] * 20
count = 0
for i, s in enumerate(students):
now_idx = 0
while 1:
if line[now_idx] > s:
break
now_idx += 1
for j in range(i-1, now_idx-1, -1):
line[j+1] = line[j]
count += 1
line[now_idx] = s
return count
def solution():
p = int(input().rstrip())
for _ in range(p):
test_case = list(map(int, input().rstrip().split()))
print(test_case[0], test(test_case[1:]))
return
solution()
"""
걸린 시간: 33분
시간 복잡도: 새로운 학생이 line에 들어올때마다 원래 있던 모든 학생이 움직이는 경우가 최대이므로
학생 수를 n이라고 하면 학생 당 (n-1), (n-2),... 0 이므로 O(n^2)인데, 이 문제는 20명 고정이므로
19*20/2 = 190 이다.
해설: 기본 line을 float('inf')로 세팅함으로써 사람이 없는 경우는 맨 뒤에 두고 없는 사람 취급을 한다.
세팅 후 순서대로 한명씩 line에 넣는데 이때 나보다 큰 가장 앞에 있는 사람을 찾아야 하기 때문에
그냥 맨 처음부터 보다가 나보다 큰 사람이 있으면 그 index에 내가 서고, 뒤에 있는 사람들을
한 칸씩 뒤로 밀면 된다. 뒤로 밀때는 지금까지 line에 있는 사람 수에서 내가 들어가야할 index까지
거꾸로 오면서 한 칸씩 뒤로 밀면 된다. 이때 count+1을 해주고 결과값 count를 반환하고 출력.
"""

View File

@@ -0,0 +1,40 @@
# 한 줄로 서기
import sys
input = sys.stdin.readline
def solution():
n = int(input().rstrip())
blank = [i for i in range(n)]
info = list(map(int, input().rstrip().split()))
result = [0] * n
for i in range(n):
result[blank[info[i]]] = i+1
blank.pop(info[i])
print(*result)
return
solution()
"""
걸린 시간: 25분
시간 복잡도: 원하는 것을 pop하는 것은 O(n)이 걸릴 수 있고, 그것을 n번 하므로 O(n^2)이다.
blank의 칸을 dict로 관리하면 O(n)으로 가능할 것 같다.
해설: 본인보다 키가 큰 사람이 왼쪽에 몇 명인지 안다는 것은 본인보다 모두가 키가 큰 구성에서는 몇 등인지 안다는 것이다.
그렇기 때문에 키가 1인 사람부터 등수를 구할 수 있고, 구한다음에는 1을 배제하고 2가 키가 제일 작은 구성에서 몇 등인지
구하는 방식으로 진행하면 된다.
이때, 이미 1이 차지한 등수를 제외하고 남은 칸들 중에 나온 등수대로 넣으면 된다.
따라서 빈 자리를 의미하는 blank 리스트에서 남는 칸들을 확인할 수 있고, info+1이 본인의 등수이지만 blank는 0부터 시작하므로
그냥 blank[info]를 하면 본인이 차지해야하는 칸이 나온다.
그 후 blank에서 차지한 칸을 지워주면 나오는 등수대로 잘 들어갈 수 있다.
"""

View File

@@ -0,0 +1,62 @@
# 주식
import sys
from collections import deque
input = sys.stdin.readline
def calculate_revenue(n, prices):
total = 0
q = deque([prices[0]])
up = 0
if prices[0] <= prices[1]:
up = 1
else:
up = 0
for i in range(1, n):
if len(q) != 0 and i == n-1 and prices[i] >= q[-1]:
high = q.pop() if i != n-1 else prices[i]
while len(q) >= 1 and q[0] > high:
q.popleft()
while len(q) >= 1:
total += (high-q.pop())
if not up:
if len(q) != 0 and prices[i] > q[-1]:
up = 1
q.append(prices[i])
continue
if len(q) != 0 and (prices[i] < q[-1] or i == n-1):
high = q.pop() if i != n-1 else prices[i]
while len(q) >= 1 and q[0] > high:
q.popleft()
while len(q) >= 1:
total += (high-q.pop())
up = 0
q.append(prices[i])
return total
def solution():
t = int(input().rstrip())
for _ in range(t):
n = int(input().rstrip())
prices = list(map(int, input().rstrip().split()))
print(calculate_revenue(n, prices))
return
solution()
"""
걸린 시간:
시간 복잡도:
해설:
"""

View File

@@ -0,0 +1,72 @@
# 집합
import sys
input = sys.stdin.readline
s = {}
all_s = {i: 1 for i in range(1,21)}
def add(x):
if s.get(x, None) != None:
return
s[x] = 1
def remove(x):
if s.get(x, None) == None:
return
s.pop(x)
def check(x):
print(s.get(x, 0))
def toggle(x):
if s.get(x, None) != None:
s.pop(x)
else:
s[x] = 1
def all():
global s
s = all_s.copy()
def empty():
global s
s = {}
def excute(exc, *arg):
if exc == "add":
add(arg[0])
elif exc == "remove":
remove(arg[0])
elif exc == "check":
check(arg[0])
elif exc == "toggle":
toggle(arg[0])
elif exc == "all":
all()
elif exc == "empty":
empty()
else:
return
def solution():
global s
m = int(input())
for _ in range(m):
q = input().rstrip().split()
exc, arg = q[0], list(map(int, q[1:]))
excute(exc, *arg)
return
solution()
"""
걸린 시간: 30분(연산 함수를 OCP로 어떻게 할지 고민하다가 그냥 했는데 나중에는 디스패치 패턴 써야겠다..)
시간복잡도: O(m)
해설: set을 써도 되긴 하는데 너무 문제가 set 그 자체라서 dictionary로 구현해보았다.
x가 1에서 20까지니까 비트마스크 20자리로 해도 가능하다고 한다.
"""

View File

@@ -0,0 +1,62 @@
# 등수 구하기
import sys
input = sys.stdin.readline
def binary_search(lst, v):
start, end = 0, len(lst)
while start < end:
mid = (start+end) // 2
if lst[mid] <= v:
end = mid
else:
start = mid+1
return start
def solution():
n, new_score, p = map(int, input().rstrip().split())
if n == 0:
print(1)
return
scores = list(map(int, input().rstrip().split()))
if n == p and new_score <= scores[-1]:
print(-1)
return
result = binary_search(scores, new_score)
print(result+1)
return
solution()
"""
걸린 시간: 1시간 (이분탐색 여러 종류를 공부하면서 하느라 좀 걸렸다..)
시간 복잡도: O(logn)
해설: 이분탐색으로 들어갈 곳을 정하고, 이때, 값이 같다면 맨 앞에 놓을 수 있도록 처음으로 lst[mid] > target이 되는 곳을 찾으면 된다.
이게 이분탐색 lower_bound이다. 여기서 찾고자 하는 것과 배열 상태에 따라 조건을 잘 설정해야되는데, 우리가 원하는 것은 3번이다.
오름차순이라면 x 이상, 초과를 찾을 것이고, 내림차순이라면 x 이하, 미만을 찾는 것이 일반화하는데 편하다.
1. 오름차순 배열에서 처음 x 이상을 찾는다면 lst[mid] >= x 이고, mid도 답이 될 수 있고, 왼쪽도 확인해봐야 하기 때문에 r = mid가 된다.
2. 오름차순 배열에서 처음 x 초과를 찾는다면 lst[mid] > x 이고, mid가 답이 될 수 있고, 왼쪽도 확인해봐야 하기 때문에 r = mid가 된다.
3. 내림차순 배열에서 처음 x 이하를 찾는다면 lst[mid] <= x 이고, mid가 답이 될 수 있고, 왼쪽도 확인해봐야 하기 때문에 r = mid가 된다.
4. 내림차순 배열에서 처음 x 미만을 찾는다면 lst[mid] < x 이고, mid가 답이 될 수 있고, 왼쪽도 확인해봐야 하기 때문에 r = mid가 된다.
아무튼 가장 중요한 것은 현재 mid가 답이 될 수 있는가에 대한 여부와 왼쪽과 오른쪽 중 어디가 답 가능성이 있는 방향인지를 보면
1. if 조건과 r, l의 매칭 & 2. r, l에 mid +1을 할지 말지를 정할 수 있다.
그 외에 이 문제는 경계에 대한 것이었기 때문에 l < r 조건에 [l, r) 구간이었는데,
원소를 특정하는 것은 mid를 보고 아니면 버리기 때문에 mid +-1은 항상 해주고, 구간도 [l, r]에 조건도 l <= r이다.
"""

View File

@@ -0,0 +1,64 @@
# 스위치 켜고 끄기
import sys
input = sys.stdin.readline
def work_mode1(s, s_state, s_num):
mul = 1
idx = s_num * mul
while idx <= s:
s_state[idx] = 1 - s_state[idx]
mul += 1
idx = s_num * mul
return
def work_mode2(s, s_state, s_num):
gap = 1
s_state[s_num] = 1 - s_state[s_num]
while s_num + gap <= s and s_num - gap >= 1:
if s_state[s_num + gap] != s_state[s_num - gap]:
break
s_state[s_num + gap] = 1 - s_state[s_num + gap]
s_state[s_num - gap] = 1 - s_state[s_num - gap]
gap += 1
return
WORK = {
1: work_mode1,
2: work_mode2
}
def solution():
s = int(input().rstrip())
s_state = [-1] + list(map(int, input().rstrip().split()))
n = int(input().rstrip())
for _ in range(n):
mode, s_num = map(int, input().rstrip().split())
now_work = WORK[mode]
now_work(s, s_state, s_num)
s_state = s_state[1:]
for i in range(s//20+1):
start, end = i*20, (i*20)+20
print(*s_state[start:end])
return
solution()
"""
걸린 시간: 16분
시간 복잡도: 남자, 여자 둘 다 한 턴에 스위치 전체 길이인 s에 대해 O(s)이므로 전체 시간 복잡도는 학생 수가 n일 때, O(s*n) 이다.
해설: 남자(mode1), 여자(mode2)로 작업을 나눠서 배수는 1,2,3 올라가면서 시작 버튼에 곱해주고, 옆으로 확인은 gap을 +1 해주면서
확인한다. while 반복문의 기본 규칙은 1과 s 사이의 범위이고, mode2에서 추가 escape는 보고 있는 두 스위치의 상태가 다를 때이다.
메인 함수에서는 mode에 맞게 work 함수를 할당 받은 후 작업을 진행하고, 20단위로 끊어서 출력한다.
추가로 worker 클래스를 만들어서 상속 받아서 하면 좀 더 안전한 코드가 될 것 같다.
"""

View File

@@ -0,0 +1,72 @@
# DFS와 BFS
import sys
from collections import deque
input = sys.stdin.readline
def dfs(n, g, v):
result = []
visited = [0]*(n+1)
stack = [v]
while stack:
w = stack.pop()
if visited[w]:
continue
result.append(w)
visited[w] = 1
for i in range(len(g[w])-1, -1, -1):
stack.append(g[w][i])
return result
def bfs(n, g, v):
result = []
visited = [0]*(n+1)
q = deque([v])
visited[v] = 1
while q:
w = q.popleft()
result.append(w)
for k in g[w]:
if not visited[k]:
q.append(k)
visited[k] = 1
return result
def solution():
n, m, v = map(int, input().rstrip().split())
g = [[] for _ in range(n+1)]
for _ in range(m):
v1, v2 = map(int, input().rstrip().split())
g[v1].append(v2)
g[v2].append(v1)
for i in range(1, n+1):
g[i].sort()
print(*dfs(n, g, v))
print(*bfs(n, g, v))
return
solution()
"""
걸린 시간: 34분
시간 복잡도: 간선 수에 따라 for문을 돌아보는 차이가 난다. 또한 어디 노드에 연결이 얼마나 되어 있느냐에 따라 sort의 차이도 난다.
해설: dfs는 시작 노드를 stack에 넣고 pop해가면서 인접 노드를 넣는 방식으로 구현이 가능하다.
이때, visited를 pop하고 찍음으로써 이전에 인접한 노드로 나왔더라도 한 번 더 나왔을 때 다시 넣을 수 있게 한다.
이는 깊이 탐색을 위해 뒤에서 나올수록 우선순위가 높아짐을 의미한다.
bfs는 시작 노드를 queue에 넣고 popleft 해가면서 인접 노드를 넣는 방식으로 구현이 가능하다.
이때, visited를 queue에 append 하면서 찍음으로써 이후에 인접한 노드로 또 나왔을 경우 다시 못 들어가게 한다.
이는 너비 탐색을 위해 먼저 나왔을수록 우선순위가 높음을 의미한다.
"""

View File

@@ -0,0 +1,61 @@
# 단축키 지정
import sys
input = sys.stdin.readline
def check_word_first(words, shortcut):
for i in range(len(words)):
c = words[i][0].upper()
if not shortcut.get(c, 0):
shortcut[c] = 1
return i
return -1
def check_word(word, shortcut):
for i in range(len(word)):
c = word[i].upper()
if not shortcut.get(c, 0):
shortcut[c] = 1
return i
return -1
def solution():
n = int(input().rstrip())
shortcut = {}
for _ in range(n):
option = input().rstrip().split()
shortcut_idx = check_word_first(option, shortcut)
if shortcut_idx != -1:
option[shortcut_idx] = f"[{option[shortcut_idx][0]}]" + option[shortcut_idx][1:]
print(*option)
continue
idx = 0
while idx < len(option):
shortcut_idx = check_word(option[idx], shortcut)
if shortcut_idx != -1:
option[idx] = option[idx][:shortcut_idx] + f"[{option[idx][shortcut_idx]}]" + option[idx][shortcut_idx+1:]
break
idx += 1
print(*option)
return
solution()
"""
걸린 시간: 41분
시간 복잡도: 완전 탐색이기 때문에 O(5*10*n)이다.
해설: 30개의 옵션에 1개 옵션 당 5개 이하 단어와 1개의 단어에 10개 이하의 알파벳이면 다 돌아봤자 1500이다.
따라서 조건대로 구현하면 된다. 대소문자를 구분하지 않기 때문에 다 upper로 만들어서 현재 사용된 옵션을 기록한다.
단어 첫 글자들을 보며 옵션 설정하고, 한 단어씩 쭉 보면서 옵션 설정을 한다.
옵션을 설정하면 출력 형태를 맞춰서 리스트에 저장하고 마지막에 한번에 출력한다.
"""

View File

@@ -0,0 +1,35 @@
# 주유소
import sys
input = sys.stdin.readline
def solution():
n = int(input().rstrip())
dist = list(map(int, input().rstrip().split()))
price = list(map(int, input().rstrip().split()))
least_price = float('inf')
result = 0
for i in range(n-1):
if price[i] < least_price:
least_price = price[i]
result += (dist[i]*least_price)
print(result)
return
solution()
"""
걸린 시간: 12분
시간 복잡도: dist 길이만큼 한 번 돌기 때문에 전체 시간복잡도는 O(n)이다.
해설: 현재 지역 다음에 본인보다 더 싼게 있으면 거기까지만 가고 그 다음부터는 싼 곳에서 기름을 사야한다.
가장 쌌던 가격을 계속 기록해가면서 price를 마지막-1 까지 순회하면 끝.
"""

View File

@@ -0,0 +1,77 @@
# 에디터
import sys
from collections import deque
input = sys.stdin.readline
def l(left_q, right_q):
if len(left_q) >= 1:
right_q.appendleft(left_q.pop())
return
def d(left_q, right_q):
if len(right_q) >= 1:
left_q.append(right_q.popleft())
return
def b(left_q):
if len(left_q) >= 1:
left_q.pop()
return
def p(left_q, w):
left_q.append(w)
return
COMMANDS = {
"L": l,
"D": d,
"B": b,
"P": p,
}
def excute(left_q, right_q, *c):
command = COMMANDS.get(c[0], None)
if c[0] == "P":
command(left_q, c[1])
elif c[0] == "L" or c[0] == "D":
command(left_q, right_q)
elif c[0] == "B":
command(left_q)
else:
return
return
def solution():
s = input().rstrip()
m = int(input().rstrip())
left_q = deque(list(s))
right_q = deque([])
for _ in range(m):
excute(left_q, right_q, *(input().rstrip().split()))
result = list(left_q) + list(right_q)
print("".join(result))
return
solution()
"""
걸린 시간: 30분
시간 복잡도: deque는 pop이나 append가 어디든 O(1)이므로 명령어 수행은 항상 O(1)이고, m번 명령을 반복하기 때문에 O(m)이다.
그 후 마지막에 두 deque를 합쳐서 출력하기 때문에 n개의 단어에서 m만큼 글자가 추가 되었다면 O(n+m)이다.
따라서 전체 시간복잡도는 O(n+m)이다.
해설: 커서를 기준으로 양쪽에 deque를 배치한 후 l, d로 커서를 움직이면 양쪽 deque에서 요소들을 옮기고, b, p로 요소를 지우거나 추가하면
커서 기준 왼쪽으로 동작한다고 했으므로 왼쪽 deque로 요소를 조정한다. 마지막에 두 deque를 list로 만들어서 합치고 출력하면 끝
확장성을 위해 dispatch 전략을 사용했지만 l,d,b,p의 인자가 달라서 excute 함수에서 또 분기를 했다. -> l,d,b,p의 인자를 같게 하면 됨
자료구조가 deque에서 다른 것으로 바뀐다면 다 수정해야 하기 때문에 결합도가 너무 높다.
-> 추상화 계층 하나를 추가해서 Editor라는 클래스를 만들고 거기다가 기능들 쓴 다음에 l,d,b,p가 그걸 받아오면 나중에 Editor 클래스만 바꾸면 됨
"""

View File

@@ -0,0 +1,40 @@
# 지름길
import sys
input = sys.stdin.readline
def solution():
n, d = map(int, input().rstrip().split())
shortcut = {}
for _ in range(n):
start, end, cost = map(int, input().rstrip().split())
if end > d:
continue
if not shortcut.get(end, []):
shortcut[end] = []
shortcut[end].append((start, cost))
dp = [0] * (d+1)
for i in range(1, d+1):
shortcut_list = shortcut.get(i, [])
dp[i] = min(min([dp[s]+c for s, c in shortcut_list]) if shortcut_list else float('inf'), dp[i-1]+1)
print(dp[d])
return
solution()
"""
걸린 시간: 30분
시간 복잡도: 모든 지점에 대해 dp 값을 구하는데, 이때 한 지점은 연결된 지름길만큼 반복한다.
하지만 지름길 하나 당 한번만 보기 때문에 전체 시간복잡도는 O(n+d)이다.
해설: 지름길의 도착 위치 기준으로 그냥 가는 것과 여러 지름길 중 하나를 선택하는 것과 같이
여러 개를 비교해야했다. 그리고 비교를 하는 것이 과거의 지점에서 계산을 하는 것이기 때문에 dp를 생각했다.
현재 지점의 dp를 계산할 때 이 곳에 올 수 있는 모든 경우의 수 중 cost가 최소인 것을 구하면 된다.
따라서 지름길을 통해 오는 것과 바로 한 칸 전 위치에서 +1로 오는 경우 중 구하면 된다.
"""

View File

@@ -0,0 +1,74 @@
# 쉬운 최단거리
import sys
from collections import deque
input = sys.stdin.readline
def find_target(grid, n, m):
for i in range(n):
for j in range(m):
if grid[i][j] == 2:
target = (i, j)
return target
return (-1, -1)
def set_zero_land(grid, visited, n, m):
for i in range(n):
for j in range(m):
if grid[i][j] == 0:
visited[i][j] = 0
return
def bfs(grid, target, n, m):
dr = [1, -1, 0, 0]
dc = [0, 0, 1, -1]
visited = [[-1]*m for _ in range(n)]
set_zero_land(grid, visited, n, m)
q = deque([target])
visited[target[0]][target[1]] = 0
while q:
r, c = q.popleft()
for i in range(4):
now_r, now_c = r+dr[i], c+dc[i]
if now_r == target[0] and now_c == target[1]:
continue
if 0 <= now_r < n and 0 <= now_c < m and grid[now_r][now_c] != 0 and visited[now_r][now_c] == -1:
q.append((now_r, now_c))
visited[now_r][now_c] = visited[r][c] + 1
return visited
def solution():
n, m = map(int, input().rstrip().split())
grid = [list(map(int, input().rstrip().split())) for _ in range(n)]
target = find_target(grid, n, m)
result = bfs(grid, target, n, m)
for i in range(n):
print(*result[i])
return
solution()
"""
걸린 시간: 55분
시간 복잡도: target 찾고, 0 세팅 하는데 O(nm)이고,
bfs는 한 노드마다 4번의 인접 노드를 확인하므로 O(4nm)
전체 시간복잡도는 O(nm)이다.
해설: 각 점에서 도착지까지 계속 찾아가는건 말이 안되고, 인접 점의 결과에 +1을 하는 식으로
O(1)에 찾도록 생각을 하였다. dp가 떠올랐지만,
target 지점을 기준으로 +1씩 해야하기 때문에 target이 중간 어딘가에 있으면 dp 테이블을 채우기 애매했다.
따라서 너비우선 탐색을 떠올렸고, bfs로 진행했다.
조건들이 조금 귀찮아서 몇 번 틀렸는데, 0인 땅은 그냥 0이고, 1인데 못 가는 땅은 -1로 출력을 해야했다.
따라서 bfs 세팅에서 기본 visited를 -1로 잡고, 0인 땅은 0으로 초기화 해주는 작업을 했다.
"""

View File

@@ -0,0 +1,69 @@
# 수 이어 쓰기
import sys
input = sys.stdin.readline
def check_parttern(now, target):
idx = 0
len_target = len(target)
for c1 in now:
while idx < len_target:
if c1 == target[idx]:
idx += 1
break
else:
idx += 1
else:
return False
else:
return True
def solution():
s = input().rstrip()
len_s = len(s)
i = 0
target = 0
while i < len_s:
target += 1
j = i
while j < len_s and int(s[i:j+1]) <= target:
now = s[i:j+1]
if check_parttern(now, str(target)):
j += 1
else:
break
i = j
print(target)
return
solution()
"""
걸린 시간: 53분
시간 복잡도: s의 길이가 3000까지가 최대라고 했기 때문에 최악의 경우 0이 3000개 있으면
target이 30000까지 간다. 즉, len(s) = n, max(target) = 10n = T이다.
근데 s의 한 숫자는 한번씩만 보는데 내 코드는 한번 볼때 다음것이 패턴이
가능하다면 중복해서 보기 때문에 O(j^2)이긴 하지만 이렇게 한번하면 그만큼 가장 최근의 것은 1번 본다.
따라서 전체 시간복잡도는 O((n+T)^2) 정도이다.
근데 s의 i번째 수를 보는 것의 중복을 없애면 O(TlogT)가 될 수 있을 것 같다.(log 밑은 10)
해설: 처음엔 문제 이해가 좀 어려웠는데 써가면서 하니까 이해가 됐다.
그리고 주어진 테스트 케이스가 엄청 복잡한 것을 보면 규칙 찾아서 하는 것이라는 걸 의심해보자.
1~10까지를 한 세트라고 했을 때 지금 보는 숫자보다 작거나 같은 숫자가 나오면 한 세트가 넘어간 것이다.
이걸로 규칙 세워서 가려고 했는데 다음 세트로 넘어갈수록 숫자가 10의 자리나 100의 자리가 생기고, 바뀌고
그러기 때문에 쉽지 않았다. -> 주어진 숫자를 쭉 보면서 답이 될 수 있는 숫자를 하나씩 키워나가자.
s의 숫자들을 하나씩 보면서 target이 될 수 있는 숫자라면, 즉 target의 substring이라면 s의 다음 숫자를 보고
target도 하나 키우는 방식으로 진행했다. 아니라면 당연히 target만 하나 키우기.
여기서 substring 구하는 방식을 s의 i번째를 확인했음에도 안쪽 while문으로 i~i+j번째까지 계속 중복 확인을 했다.
이 부분을 따로 함수로 빼서 고치든가 했어야 했다.(어차피 그래도 답은 틀림)
아무튼 나는 now in target 방법으로 진행했는데, 반례가 존재했다. s의 11이 target의 101을 대표하지 못했다.
in으로는 0이 있어서 안되기 때문이다.
따라서 target의 한 글자와 s의 한글자씩 보는 방식으로 진행했다.
역시 i~i+j번째를 보기까지 중복이 있긴 해서 이 부분을 고쳐야 하긴 하지만 아무튼 통과했다.
"""

View File

@@ -0,0 +1,32 @@
# 문자열 교환
import sys
input = sys.stdin.readline
def solution():
s = input().rstrip()
k = s.count("a")
k_count = {"a": s[:k].count("a"), "b": s[:k].count("b")}
circle = s + s
result = k_count["b"]
for i in range(1, len(circle)-k):
k_count[circle[i-1]] -= 1
k_count[circle[i+k-1]] += 1
result = min(result, k_count["b"])
print(result)
return
solution()
"""
걸린 시간: 못 품
시간 복잡도: 전체에서 a의 개수(k)를 세고, k구간씩 옮겨가며 b개수를 슬라이딩 윈도우로 업데이트하기 때문에 전체 시간복잡도는 O(n)이다.
해설: 원형이라고 했기 때문에 같은 것 2배를 해준 문자열을 만든다. 여기서 기존 a가 다 연속이면 되기 때문에
a의 개수 k를 구해서 원형에서 k구간씩 확인하며 b를 다 빼주면 되는데, 이때 b개수의 최소값이 답이다.
"""

View File

@@ -0,0 +1,44 @@
# 숨바꼭질
import sys
from collections import deque
input = sys.stdin.readline
def solution():
n, k = map(int, input().rstrip().split())
max_len = 100000
line = [0 for _ in range(max_len+1)]
move = [-1, 1]
q = deque([n])
line[n] = 1
while q:
now = q.popleft()
for m in move:
now_move = now + m
if 0 <= now_move < max_len+1 and line[now_move] == 0:
q.append(now_move)
line[now_move] = line[now] + 1
if 0 <= now*2 < max_len+1 and line[now*2] == 0:
q.append(now*2)
line[now*2] = line[now] + 1
print(line[k]-1)
return
solution()
"""
걸린 시간: 몰라
시간 복잡도: 한 지점 당 큐에 한 번씩만 들어갔다가 나오기 때문에 전체 지점만큼의 시간이 걸린다. 따라서 O(100000)이다.
해설: 특정 위치를 볼 때 그 위치로부터 //2, -1, +1 지점에서 +1 한 것들과 기존 지점까지 총 4개 값의 최소값을 가져오면서 dp 테이블을
갱신하면 될 것 같았는데, 이렇게 되면 dp 테이블을 채우는 방향에 따라 비교해야하는 4지점이 다 안 채워진 경우가 있다.
따라서 bfs로 한번 이동하는 것을 하나의 계층으로 생각해서 진행하면 된다.
"""

View File

@@ -0,0 +1,36 @@
# 어두운 굴다리
import sys
input = sys.stdin.readline
def solution():
n = int(input().rstrip())
m = int(input().rstrip())
x = list(map(int, input().rstrip().split()))
result = 0
for i in range(1, m):
now = (x[i]-x[i-1])//2 if (x[i]-x[i-1])%2==0 else (x[i]-x[i-1])//2 + 1
result = max(result, now)
result = max(result, x[0]-0, n-x[-1])
print(result)
return
solution()
"""
걸린 시간: 26분
시간 복잡도: 0에서 첫 번째 위치를 빼는 것이랑, 끝에서 마지막 위치를 빼는 것을 포함하여 약 m번의 연산을 진행한다.
m이 n보다 작으므로 전체 시간복잡도는 O(n)이다.
해설: 가로등이 놓일 위치 사이의 거리가 가장 큰 값을 2로 나눈 값이 답이다. 반씩 나눠서 그 구간을 커버해야하기 때문이다.
x를 순차적으로 가면서 앞의 위치와 차이//2 중 최대를 찾으면 되는데, 이때, 차이가 홀수인 경우 +1을 해줘야 커버가된다.
삼항연산자로 구분했지만 ceil을 하거나 -(-n//2)를 하면 같은 효과를 볼 수 있다.
파이썬의 //는 무조건 음의 무한대로 내림하기 때문에 -5 // 2는 -2.5가 -3이 된다. => 올림 효과를 볼 수 있다.
마지막으로 0과 n에는 가로등 하나가 온전히 감당해야 하므로 마지막에 비교해주면 끝.
"""

View File

@@ -0,0 +1,99 @@
# 진우의 달 여행
import sys
input = sys.stdin.readline
def solution():
n, m = map(int, input().rstrip().split())
direction = [-1, 0, 1]
grid = [list(map(int, input().rstrip().split())) for _ in range(n)]
dp = [[[0]*3 for _ in range(m)] for _ in range(n)]
for i in range(m):
for j in range(len(direction)):
dp[0][i][j] = grid[0][i]
for i in range(1, n):
for j in range(m):
for k in range(len(direction)):
if j == 0:
dp[i][j][k] = min([dp[i-1][j+direction[h]][h] for h in range(len(direction)) if h != k and direction[h] != -1]) + grid[i][j]
elif j == m-1:
dp[i][j][k] = min([dp[i-1][j+direction[h]][h] for h in range(len(direction)) if h != k and direction[h] != 1]) + grid[i][j]
else:
dp[i][j][k] = min([dp[i-1][j+direction[h]][h] for h in range(len(direction)) if h != k]) + grid[i][j]
print(min([min(dp[n-1][j]) for j in range(m)]))
return
solution()
"""
풀이(2) -> dp
걸린 시간: 45분
시간 복잡도: n*m*3(방향)으로 된 3차원 dp를 채우는데, 방향을 채울 때 본인을 제외한 방향들의 최소를 구하기 때문에 *2를 해서 (n*m*3*2)이다.
해설: 다른 풀이에서 DP가 있다는 것을 알게되어서 두 번째 풀이를 진행했다.
i, j, k는 행, 열, 이전 방향이라고 할때, dp[i][j][k]를 구하면 i-1번째 행에서 direction[k]방향을 제외한 j열의 값들 중에 최소와
현재 행렬 값(grid[i][j])의 합이다.
즉 [i][j][0]은 direction[0]이 -1방향이므로 [i-1][j-1]에서 값을 가져오는데 [i-1][j-1][0]을 제외한 값들 중에 최소를 가져온다.
"""
"""
풀이(1) -> dfs
걸린 시간: 35분
시간 복잡도: 첫 행에서 갈 수 있는 경우의 수는 3이고 마지막 행을 제외한 행에서 경우의 수는 2이므로 3*2^(n-2) 이다.
이것을 열 개수만큼 반복해야 하므로 3*2^(n-2)*m 이다. 사실상 O(2^n * m)이라서 말이 안되는 시간복잡도이지만 조건때문에 그냥 했다.
해설: 2 <= n, m <= 6인걸 봐서는 모든 경우의 수를 확인해보라는 것 같다. (3 * 2^4) * 6 개이므로 얼마 안된다.
dfs의 기저를 depth가 n-1보다 클때, 즉 마지막 행에 도달했을 때로 잡았고, 지금까지 더해온 total에 마지막 grid 값을 더한 뒤
result 리스트에 넣고 마지막에 min을 구해서 출력하도록 하였다.
이전에 어디로 갔는지도 계속 가져가며 갈 수 있는 전체 방향에서 이전에 간 방향 빼고, 열 밖으로 나가는 것까지 빼면 된다.
total을 어떻게 계속 갱신할지 dfs에 선언하면 꼬일 것 같아서 고민했는데 그냥 인자로만 계속 넘기면 괜찮은 것 같다.
"""
# result = []
# def solution():
# n, m = map(int, input().rstrip().split())
# grid = [list(map(int, input().rstrip().split())) for _ in range(n)]
# dx = [-1, 0, 1]
# dy = [1, 1, 1]
# def dfs(depth, col, before, total):
# if depth >= n-1:
# total += grid[depth][col]
# result.append(total)
# return
# for i in range(3):
# if i == before or col+dx[i] >= m or col+dx[i] < 0:
# continue
# dfs(depth+dy[i], col+dx[i], i, total+grid[depth][col])
# return total
# for i in range(m):
# dfs(0, i, -1, 0)
# print(min(result))
# return
# solution()

View File

@@ -0,0 +1,51 @@
# 볼 모으기
import sys
input = sys.stdin.readline
def count_result(n, balls, left, right, ball_count):
left_count, right_count = 0, 0
left_idx, right_idx = 0, n-1
while left_idx <= n-1 and balls[left_idx] == left:
left_count += 1
left_idx += 1
while right_idx >= 0 and balls[right_idx] == right:
right_count += 1
right_idx -= 1
return min(ball_count[left]-left_count, ball_count[right]-right_count)
def solution():
n = int(input().rstrip())
balls = list(input().rstrip())
ball_count = {"R": 0, "B": 0}
for b in balls:
ball_count[b] += 1
result = min(count_result(n, balls, "R", "B", ball_count), count_result(n, balls, "B", "R", ball_count))
print(result)
return
solution()
"""
걸린 시간: 50분
시간 복잡도: count_result가 왼쪽, 오른쪽에 대해 첫 묶음이 몇 개인지 계산하는데 최악의 경우 전체이므로,
O(n)이다. 처음에 ball_count를 셀 때 O(n)이고, count_result가 2번이므로 O(3n), 전체 시간복잡도는 O(n)이다.
해설: 같은 것끼리 묶인 최종 상태는 한쪽 끝이 B, R 하나씩 있어야 된다.
따라서 큰 경우의 수는 양 끝이 R, B 이거나 B, R인 경우이다.
이제 각 경우의 수마다 R을 넘길지 B를 넘길지 계산하면 되는데,
정해진 경우의 끝에 따라서 같은 묶음만 뺀 개수를 세면 된다.
경우의 수 1) R, B일때 왼쪽 R 묶음 제거한 R 개수, 오른쪽 B 묶음 제거한 B개수 중 최소
경우의 수 2) B, R일때 왼쪽 B 묶음 제거한 B 개수, 오른쪽 R 묶음 제거한 R개수 중 최소
두 경우의 수 중 최소 값 구하면 된다.
"""

View File

@@ -0,0 +1,35 @@
# 최소 힙
import sys
import heapq
input = sys.stdin.readline
def solution():
n = int(input().rstrip())
h = []
for i in range(n):
v = int(input().rstrip())
if v == 0:
if len(h) == 0:
print(0)
continue
print(heapq.heappop(h))
else:
heapq.heappush(h, v)
return
solution()
"""
걸린 시간: 5분
시간 복잡도: heappush와 heappop 모두 현재 트리의 높이 즉, logn만큼 든다.
트리 길이가 점점 길어져서 n까지 가는 것이기 때문에 평균적으로 O(logn)이다.
n번 진행하기 때문에 전체 시간복잡도는 O(nlogn)이다.
해설: 대놓고 힙 써서 구현하라고 해서 쉬웠다.
0일때 pop하는데, 배열 길이가 0이면 0 출력하도록 설정
넣을때는 heappush, 뺄때는 heappop 하면 끝
"""

View File

@@ -0,0 +1,58 @@
# IF문 좀 대신 써줘
import sys
input = sys.stdin.readline
def binary_search(target, badge):
l, r = 0, len(badge)
while l < r:
mid = (l+r)//2
if badge[mid][1] >= target:
r = mid
else:
l = mid + 1
return l
def solution():
n, m = map(int, input().rstrip().split())
badge = []
name, value = input().rstrip().split()
badge.append((name, int(value)))
for i in range(n-1):
name, value = input().rstrip().split()
if badge[-1][1] == int(value):
continue
badge.append((name, int(value)))
for i in range(m):
target = int(input().rstrip())
idx = binary_search(target, badge)
print(badge[idx][0])
return
solution()
"""
걸린 시간: 40분
시간 복잡도: m개의 값에 대해 n범위에서 이분탐색을 진행하기 때문에 O(mlogn)
해설: 문제가 너무 단순해서 뭔가 했는데 n, m <= 10^5 이기 때문에 n에서 비교를 최대한 줄이는 것이 핵심이다.
n으로 만들어진 수직선 상에 m이 들어갈 위치를 찾으면 되기 때문에 이분탐색으로 진행한다.
10^5는 17정도에서 끝낼 수 있다. badge에 상한 값이 겹치는 애들은 첫번째만 넣고, m개 받으면서 이분탐색 진행
이분탐색 정리했는데 자꾸 헷갈리니까 다시 정리
내가 찾고자 하는건 badge의 idx이다. 즉 mid 기준으로 생각하는게 편하다.
지금 문제는 target이 어느 상한에 속하나인데, 100이라면 100이상의 상한을 찾아서 가장 먼저 나오는
상한값이 답인 것이다. 따라서 badge[mid][1] >= target으로 기본을 잡으면 되고,
저게 성립하면 mid는 답이 될 수 있지만 왼쪽에 답이 있을 수도 있다.
따라서 r을 움직여야하고, 현재 mid를 포함한 값이 되야 한다. r = mid
반대 상황은 badge가 target보다 작은거니까 현재 mid가 답이 될 수 없으므로 l = mid + 1
원소의 유무 판단이 아닌 들어갈 곳 찾기기 때문에 l, r = 0, len(badge)로 한다.
while문 기본 조건은 들어갈 자리를 찾는 것이기 때문에 l = r일때는 알아보는 것이 의미 없다. 따라서 while l < r
끝.
"""

View File

@@ -0,0 +1,78 @@
# 햄버거 분배
import sys
from collections import deque
input = sys.stdin.readline
def clear_left(d, i, k):
while len(d) >= 1 and d[0][0] < i - (k):
d.popleft()
return
def clear_right(d, i, k):
while len(d) >= 1 and d[-1][0] < i - (k):
d.pop()
return
def manage_h(d, i, t, result):
if len(d) >= 1 and d[-1][1] == "P":
d.pop()
result += 1
else:
d.append((i, t))
return result
def manage_p(d, i, t, result):
if len(d) >= 1 and d[0][1] == "H":
d.popleft()
result += 1
else:
d.appendleft((i, t))
return result
MANAGEMENT = {
"H": manage_h,
"P": manage_p
}
def solution():
n, k = map(int, input().rstrip().split())
table = input().rstrip()
d = deque()
result = 0
for i, t in enumerate(table):
clear_left(d, i, k)
clear_right(d, i, k)
manager = MANAGEMENT.get(t, None)
if not manager:
return -1
result = manager(d, i, t, result)
print(result)
return
solution()
"""
걸린 시간: 35분
시간 복잡도: deque의 길이를 k로 유지하면서 테이블에 있는 하나의 요소는 deque에 1번만 들어오거나 나가기 때문에
한 요소 당 시간 복잡도는 O(1)이고 전체 요소에 대해서 진행하기 때문에 전체 시간복잡도는 O(n)이다.
해설: P, H가 짝을 지어서 사라지면 된다고 생각하여 스택을 떠올렸고, 오래된 H나 P는 선택할 수 없게 지워버리는 방법을 생각했을 때
큐가 생각나서, 합쳐서 deque를 사용하기로 하였다.
처음에는 오래된 것들을 나가는 방향을 popleft, P/H가 들어오는 곳은 append, 짝을 이뤄서 나가는 방향을 pop으로 생각했는데,
이러면 과거 H를 P가 먹을 수 있는 상황에서 가장 최신 H만 먹기 때문에 최선이 아니다.
따라서 P는 들어오는 것과 짝 맞춰서 나가는 것을 왼쪽, H는 오른쪽으로 함으로써 우선순위를 오래 기다린 사람과 오래된 햄버거로 하였다.
그리고 이번에 들어오는 요소는 k 범위 이전 것들과는 전혀 상호작용할 수 없으므로 매번 clear를 왼쪽, 오른쪽을 다 해준다.
참고: len(list, deque, set, dict)들 모두 원소의 개수를 따로 저장하고 있기 때문에 O(1)이다.
"""

View File

@@ -0,0 +1,47 @@
# 랭킹전 대기열
import sys
input = sys.stdin.readline
def solution():
p, m = map(int, input().rstrip().split())
room = {}
for i in range(p):
l, n = input().rstrip().split()
l = int(l)
for j in room:
if len(room[j]) < m and l >= j[0]-10 and l <= j[0]+10:
room[j].append((l, n))
break
else:
room[(l, n)] = [(l, n)]
for v in room.values():
if len(v) == m:
print("Started!")
else:
print("Waiting!")
for j in sorted(v, key=lambda x: x[1]):
print(*j)
return
solution()
"""
걸린 시간: 50분
시간 복잡도: p개의 플레이어를 방에 분배하기 위해 각 방을 확인해보는 시간 복잡도도 균등분배 기준으로 p//m개 방이므로, O(p* p//m) = O(p^2*m)이다.
room.values()를 확인하며 정렬하고 출력하는 것은 p//m 즉 균등분배 가정으로 나올 수 있는 최대 방 개수만큼 정렬하기 때문에
O(mlogm * p//m)이므로 O(plogm)이다.
따라서 전체 시간 복잡도는 O(p^2*m)이다.
p, m <= 300이기 때문에 그냥했지만 크기가 커진다면 모르기 때문에 들어갈 방을 범위로 나타내서 이분탐색을 하면 줄일 수 있다.
물론 방이 다 차면 같은 범위를 가지는 방이 또 필요할 수 있기 때문에 적절히 조치를 취해야한다.
해설: p개의 플레이어를 확인하면서 맞는 room이 있다면 넣고, 없다면 room을 만든다. 이때, 딕셔너리를 사용했는데 level만 키로 쓰면 중복될 수 있으므로,
(level, name)을 키로 써서 해야한다. 플레이어마다 생성된 room을 순서대로 다 범위를 확인한다. (이분 탐색을 쓰면 더 좋겠다.)
다 room에 넣은 후 room 길이에 따라 started와 waiting을 쓰고, 이름 순으로 정렬한 뒤 출력한다.
"""

View File

@@ -0,0 +1,72 @@
# 쿠키의 신체 측정
import sys
input = sys.stdin.readline
def find_heart(grid, n, k):
for i in range(n):
if grid[k][i] == "*":
return [k+1, i]
else:
return [-1, -1]
def down_check_star(grid, n, x, y):
length = 0
while x <= n-1:
if grid[x][y] != "*":
break
length += 1
x += 1
return length
def row_check_star(grid, n, x, y, dir): # 오른쪽 +1, 왼쪽 -1
length = 0
while y <= n-1 and y >= 0:
if grid[x][y] != "*":
break
length += 1
y += dir
return length
def solution():
n = int(input().rstrip())
grid = []
heart = [-1, -1]
left_arm = 0
right_arm = 0
mid = 0
left_leg = 0
right_leg = 0
for i in range(n):
grid.append(input().rstrip())
if heart[0] == -1:
heart = find_heart(grid, n, i)
left_arm = row_check_star(grid, n, heart[0], heart[1]-1, -1)
right_arm = row_check_star(grid, n, heart[0], heart[1]+1, +1)
mid = down_check_star(grid, n, heart[0]+1, heart[1])
left_leg = down_check_star(grid, n, heart[0]+mid+1, heart[1]-1)
right_leg = down_check_star(grid, n, heart[0]+mid+1, heart[1]+1)
heart[0] += 1
heart[1] += 1
print(*heart)
print(left_arm, right_arm, mid, left_leg, right_leg)
return
solution()
"""
걸린 시간: 30분
시간 복잡도: 팔, 다리, 허리는 일자로 있기 때문에 O(n)이지만, 머리는 (0,0) 부터 쭉 찾아서 처음 나오는
*이므로 O(n^2)이다. 따라서 전체 시간 복잡도는 O(n^2)이다.
해설: (0,0)에서 선형으로 찾다가 가장 먼저 나오는 *이 머리이다. 이 머리 바로 한 칸 밑이 심장이다.
심장에서 팔, 다리, 허리를 구하면 되는데, 연속된 *을 찾는 것이다.
이는 행으로 연속된 별 찾기와 열로 연속된 별 찾기 함수 2개를 만들어서 진행하면 된다.
위로 올라갈 일은 없어서 열은 그냥 down으로 했고, 행은 row로 방향을 인자로 받아서 체크했다.
"""

View File

@@ -0,0 +1,61 @@
# 타노스
import sys
from collections import deque
input = sys.stdin.readline
def solution():
s = input().rstrip()
s_dict = {"0": [], "1": []}
len_0 = 0
len_1 = 0
for i in range(len(s)):
if s[i] == "0":
s_dict["0"].append(i)
len_0 += 1
else:
s_dict["1"].append(i)
len_1 += 1
len_0 //= 2
len_1 //= 2
s_dict["0"] = deque(s_dict["0"][:len_0])
s_dict["1"] = deque(s_dict["1"][len_1:])
new_s = ""
for _ in range(len_0+len_1):
idx_0 = s_dict["0"][0] if len_0 > 0 else float('inf')
idx_1 = s_dict["1"][0] if len_1 > 0 else float('inf')
if idx_0 > idx_1:
s_dict["1"].popleft()
len_1 -= 1
new_s += "1"
else:
s_dict["0"].popleft()
len_0 -= 1
new_s += "0"
print(new_s)
return
solution()
"""
걸린 시간: 40분
시간 복잡도: s길이만큼 개수를 세고, s길이의 반만큼 재배열을 하기 때문에 O(len(s))이다. 그런데 나처럼 popleft 하면서 하지 않고,
그냥 두 리스트 합친다음에 sort하면 nlogn이므로 내가 더 낫다.
해설: 새로운 문자열 s는 기존 s에서 재배열 하는 것이 아니라 그냥 빼는 것이었다. 문제 설명 개별로네..
아무튼 그래서 개수를 세고 1은 앞에 있는 것부터 반만큼 지우고, 0은 뒤에 있는 것부터 반만큼 지우면 된다.
그렇게 하기 위해서 0과 1의 인덱스를 따로 기록해두고 0의 인덱스 리스트는 앞에서부터 반 자르고, 1은 반부터 뒤까지 자른다.
마지막으로 이 인덱스 리스트 2개를 맨 앞에 것들끼리 비교해서 작은 놈을 popleft 하고, 그에 맞는 숫자(0 또는 1)을 new_s에 추가한다.
deque는 슬라이스를 지원하지 않아서, 리스트에서 슬라이스하고 popleft 하기 전에 deque로 만들었다.
"""

View File

@@ -0,0 +1,55 @@
# N번째 큰 수
import sys
import heapq
input = sys.stdin.readline
def solution():
n = int(input().rstrip())
rank = list(map(int, input().rstrip().split()))
heapq.heapify(rank)
for _ in range(n-1):
nums = list(map(int, input().rstrip().split()))
for i in range(n):
heapq.heappush(rank, nums[i])
heapq.heappop(rank)
print(heapq.heappop(rank))
return
solution()
"""
걸린 시간: 50분(메모리 때문에 고민 좀 했다.)
시간 복잡도: O(n^2logn) n^2개의 수를 읽을때마다 n개 길이의 힙에서 push와 pop을 하기 때문에 정렬 하는 것까지 logn해서 O(n^2logn)이다.
해설: 첫 번째 코드는 맨 마지막 행부터 n개중에 1등을 뽑고 1등이 나온 열에서 바로 한칸 위에 애를 데리고 또 n개중에 1등을 뽑고 총 n번을 진행하려고 했다.
그런데 결국 이게 O(n^2)이라는 것을 깨닫고 여기서 n개 중에 1등을 뽑는 방식을 줄이기 위해 heap을 생각해냈다. 그러면 1등 뽑을 때 O(logn)에 할 수 있다.
다른 문제가 하나 있는데 메모리 제한이 12MB라서 처음에 n^2개의 수들을 리스트에 저장하지 말고 한 줄씩 읽는대로 처리해야했다.
따라서 한 줄씩 heap에 넣으며 1등만 데리고 다음 줄로 넘어가서 확인하는 방식으로 하려고 했는데 이건 마지막 라인에서 경합 자격도 없는 애가 승부를
벌이게 되는 논리적 모순이 생기기 때문에 그냥 하나씩 계속 읽으면서 길이 n을 유지하는 최소 힙에 넣고 마지막에 heappop 하면 된다.
"""
# def solution():
# n = int(input().rstrip())
# nums = [[] for _ in range(n)]
# for _ in range(n):
# row = input().rstrip().split()
# for i in range(n):
# nums[i].append(int(row[i]))
# rank = [(-nums[i].pop(), i) for i in range(n)]
# heapq.heapify(rank)
# for i in range(n-1):
# _, nums_idx = heapq.heappop(rank)
# heapq.heappush(rank, (-nums[nums_idx].pop(), nums_idx))
# print(-heapq.heappop(rank)[0])
# return

View File

@@ -0,0 +1,59 @@
# 영단어 암기는 괴로워
import sys
import heapq
input = sys.stdin.readline
def set_priority(words, word):
word_info = words.get(word, None)
frequency, length = 0, 0
if word_info:
frequency = -(word_info[0]) + 1
length = -(word_info[1])
else:
frequency = 1
length = len(word)
words[word] = [-frequency, -length, word, 0]
return words[word]
def solution():
n, m = map(int, input().rstrip().split())
words = {}
word_list = []
for i in range(n):
word = input().rstrip()
if len(word) < m:
continue
heapq.heappush(word_list, set_priority(words, word))
for i in range(len(word_list)):
f, l, w, is_pop = heapq.heappop(word_list)
if words.get(w)[3]:
continue
else:
print(w)
words[w][3] = 1
return
solution()
"""
걸린 시간: 35분
시간 복잡도: set_priority는 단어 길이를 세고, 정보를 dict에서 읽고 쓰는 것인데 m은 최대 10이므로 전체 시간복잡도는 O(1)이다.
solution 함수에서 n개의 단어를 보며 set_priority를 실행하고, heap에 push하므로 요소당 O(logn)이기 때문에 O(nlogn)이다.
마지막으로 word_list를 확인할 때도 heap에서 pop이므로 O(nlogn)이다. 따라서 전체 시간복잡도는 O(nlogn)이다.
해설: heap에서 정렬할 때 우선순위 3가지로 정렬하도록 리스트를 넣는다.
그러기 위해 word_info dict을 만들어서 정보 업데이트를 O(1)에 한다.
단어마다 정보를 업데이트 해가며 heap에 넣고, 다 넣은 다음 heappop 하면서 기존에 안 나온 것들만 출력을 한다.
좀 빠르게 해보겠다고 heap 써서 O(nlogn)으로 했지만 그냥 Counter 하고, 우선순위 역순으로 sort 3번 하면 이것도 O(nlogn)이다...
"""

View File

@@ -0,0 +1,44 @@
# 겹치는 건 싫어
import sys
from collections import deque
input = sys.stdin.readline
def solution():
n, k = map(int, input().rstrip().split())
s = list(map(int, input().rstrip().split()))
idx = 0
c = {}
d = deque([])
result = 0
while idx < len(s):
new = s[idx]
if not c.get(new, 0):
c[new] = 0
d.append(new)
c[new] += 1
if c[new] > k:
while d and c[new] > k:
now = d.popleft()
c[now] -= 1
result = max(result, len(d))
idx += 1
print(result)
return
solution()
"""
걸린 시간: 15분
시간 복잡도: 한 요소가 deque에 들어갔다가 나오기를 한번씩만 하므로 O(n)이다.
해설: deque에 숫자를 넣고, 숫자마다 개수를 dict로 센다.
숫자를 deque에 넣었을 때 그 숫자의 개수가 k개를 넘으면 그 숫자가 k개 이하가 될때까지 deque에서 popleft를 한다.
매번 현재 길이를 최대 길이와 비교해간다.
"""

View File

@@ -0,0 +1,38 @@
# 카드2
import sys
from collections import deque
input = sys.stdin.readline
def solution():
n = int(input().rstrip())
cards = deque([i for i in range(1, n+1)])
drop_count = 0
while drop_count < n-1:
cards.popleft()
drop_count += 1
cards.append(cards.popleft())
print(cards.pop())
return
solution()
"""
걸린 시간: 13분
시간 복잡도: deque에 넣고 빼는 작업은 O(1)이므로, 전체 시간복잡도는 O(n)이다.
해설: deque에 넣고 진행할 경우 앞, 뒤에 대한 추가, 삭제 연산이 O(1)이므로 이 자료구조를 활용해서 상황을 그대로 구현하면 된다.
규칙을 좀 찾아보면 먼저 홀수를 다 버리고, 짝수만 남은 상태에서 또 지워나가는 규칙을 찾을 수 있어서 수학적으로 풀 수 있을 것 같지만,
이 문제를 서비스 자체라고 생각하면 조건이 어떻게 바뀔지 모르는 상태에서 규칙을 찾아서 그 규칙에만 맞는 코드를 짜는 것보다는
시뮬레이션쪽으로 설계함으로써 사람이 하기 힘든 반복 작업을 자동화하는 것이 더 목적성이 맞다고 생각한다.
"""

View File

@@ -0,0 +1,46 @@
# 블로그
import sys
from collections import deque, Counter
input = sys.stdin.readline
def solution():
n, x = map(int, input().rstrip().split())
one_days = list(map(int, input().rstrip().split()))
duration = deque([one_days[i] for i in range(x)])
duration_result = [0] * n
duration_result[x-1] = sum(duration)
for i in range(x, n):
before = duration.popleft()
duration.append(one_days[i])
after = duration[-1]
duration_result[i] = duration_result[i-1] - before + after
c = Counter(duration_result)
max_vistor = max(c)
if max_vistor == 0:
print('SAD')
else:
print(max_vistor)
print(c.get(max_vistor))
return
solution()
"""
걸린 시간: 19분
시간 복잡도: 전체 요소를 한, 두바퀴 정도 돌기 때문에 O(n)이다.
해설: 그냥 인덱스로 앞 뒤로 줄이고, 늘이면 되는데 슬라이딩 윈도우는 deque를 쓰는게 좀 직관적이라서 deque를 사용했다.
최대값과 count를 계속 업데이트 하는 것이 코드가 가독성이 떨어지는 것 같아서 어차피 그렇게 연산하는 것과
마지막에 결과 리스트 한 바퀴 돌면서 개수 세는 연산이 비슷하기 때문에 그냥 결과 리스트에 쭉 저장하고 Counter로 개수를 셌다.
0이 최대일 경우 SAD 출력(처음에 이거 안해서 틀림), 아니면 최대값과 그 개수를 출력한다.
"""

View File

@@ -0,0 +1,38 @@
# 가희와 키워드
import sys
input = sys.stdin.readline
def solution():
n, m = map(int, input().rstrip().split())
memo = set()
for _ in range(n):
memo.add(input().rstrip())
for _ in range(m):
for keyword in input().rstrip().split(","):
if keyword in memo:
memo.remove(keyword)
print(len(memo))
return
solution()
"""
걸린 시간: 20분
시간 복잡도: 한 글에 최대 키워드가 10개이므로, set으로 차집합을 구하면 두 집합 중 작은 것만큼 시간이 드는데,
작은 것의 최대 길이가 10이므로 O(10m)이다. set의 길이는 key의 개수이므로 O(1)이기 때문이다.
해설: 처음에는 memo - keyswords를 했는데 이 연산은 비파괴 연산이라 기존 set들을 유지해야하므로
차집합된 set을 새로 만든다. 따라서 memo 길이만큼의 시간이 든다.
그래서 in으로 있는지 확인(O(1))하고, remove(O(1))로 지웠다.
remove는 key가 없으면 keyerror가 나는데 그래서 나는 있을 때만 지워서 괜찮았고,
discard를 쓰면 있으면 지우고 없으면 그냥 가만히 있는다고 한다.
"""

View File

@@ -0,0 +1,59 @@
# 창고 다각형
import sys
input = sys.stdin.readline
def solution():
n = int(input().rstrip())
line = sorted([tuple(map(int, input().rstrip().split())) for _ in range(n)])
high_stack = []
result = 0
for x, y in line:
if not high_stack:
high_stack.append((x, y))
continue
last_pop = ()
while len(high_stack) != 0 and high_stack[-1][1] <= y:
last_pop = high_stack.pop()
if len(high_stack) == 0:
result += (x - last_pop[0])*last_pop[1]
high_stack.append((x, y))
for _ in range(len(high_stack)):
x, y = high_stack.pop()
if len(high_stack) == 0:
result += y
else:
result += (x - high_stack[-1][0])*y
print(result)
return
solution()
"""
걸린 시간: 40분
시간 복잡도: while문이 있지만 어차피 전체적으로는 요소 기준으로 봤을 때 stack에 들어가고 나오는거 한번씩이기 때문에 O(n)이다.
해설: 가장 높은 놈(top)을 만날때까지는 그냥 max 갱신해가며 하면 되지만 내리막에서는 작은 놈을 갱신하며 가야된다.
근데 내려갈때 무지성으로 작은 놈만 갱신해가면, 갑자기 큰 놈이 나왔을 때 우물형태가 생기므로 조건에 위배되기 때문에 지금까지 구했던것을 다시 계산해야된다.
근데 그러면 돌아가서 다시 계산하기 때문에 O(n^2)이 되버린다.
따라서 작은 놈을 계속 가져가되, 그것보다 큰 놈이 나왔을 때 이전 작은 것들을 버리는 방식으로 하면 된다. 이건 stack을 쓰면 쉽게 구현이 가능하다.
따라서 스택에 넣는 조건을 내가 들어갈때 스택에(이전 놈들) 나보다 작은 애들은 다 pop하고 들어간다. 그렇게 끝에는 top과 완벽한 내리막만 남는다.
이제 stack에서 pop을 하면서 pop한 녀석과 아직 stack에 남아있는 놈과의 거리로 넓이를 계산하고 마지막에 나오는 녀석은 너비가 1이므로 계산하면 된다.
한 가지 문제는 오르막에서도 이 조건을 그대로 쓰면 나보다 작은 이전 놈들의 권리가 없어진다.
따라서 내가 스택에 들어갈 때 나보다 작은 놈들을 다 pop했는데 stack이 비어 있다면 아직 top이 없었던 것이기 때문에 마지막에 나온 애는 넓이를 계산해준다.
다른 풀이로는 가장 높은 놈을 기준으로 오르막과 내리막이 나올 수밖에 없으므로, 왼쪽에서 top까지 max를 갱신해가며 넓이 구하고,
오른쪽에서 top까지 반대로 가면서 max 갱신해가며 넓이 구해서 더하면 끝이긴 하네.
"""

View File

@@ -0,0 +1,56 @@
# 예산
import sys
input = sys.stdin.readline
def solution():
n = int(input().rstrip())
req = list(map(int, input().rstrip().split()))
m = int(input().rstrip())
if sum(req) <= m:
print(max(req))
return
now_ceil = m // n
now_asset = m
sorted_req = sorted(req)
idx = 0
while idx < n-1 and now_ceil >= sorted_req[idx]:
now_asset -= sorted_req[idx]
idx += 1
now_ceil = now_asset // (n-idx)
print(now_ceil)
return
solution()
"""
걸린 시간: 1시간 10분
시간 복잡도: 내 풀이는 정렬할 때 O(nlogn)이고, n개의 요청예산을 보기 때문에 O(n)이라서 전체 시간복잡도는 O(nlogn)이다.
이분탐색 풀이의 경우는 0부터 max(req) 범위에서 찾기 때문에 log(max(req))이고, 매 mid마다 req 내용을 다 확인하므로
O(nlog(max(req)))이다.
문제에서는 req <= 10^5이고, n <= 10000이기 때문에 log로 치면 내가 더 빠르지만 이 조건이 바뀌는 것에 따라 달라질 것 같다.
해설: 요청 예산의 합이 m보다 작거나 같다면 그냥 다 주고 요청 예산 중 최대값을 출력하면 된다.
근데 아니라면 두 가지 풀이 방법이 있다.
먼저 내가 처음 푼 방식은 요청 예산들을 오름차순으로 정렬한 후 처음 상한액을 평균값으로 매긴다.
그 다음 한 곳씩 예산을 나눠주는데 이때 상한액 이하의 곳들은 나눠주고 남은 예산으로 남은 곳의 수를 나눠서 다시 평균값을 상한액으로 바꾼다.
이 과정을 반복하다가 현재 상한액보다 많은 곳을 요청한 곳이 있으면 앞으로 뒤에 있는 곳들도 요청한만큼 채워줄 수 없기 때문에
이번 상한액이 답이다.
근데 이분탐색으로 푸는 방법이 있음.
전체 요청 예산의 범위 중 상한액을 이분 탐색으로 찾아가는 것임.
한 점을 찾는 것이기 때문에 l <= r 조건에서 [mid]를 설정하고, n개의 요청 예산을 설정한 mid 기준으로 다 분배해본 후,
m 이하 중 최대값을 찾으면 된다.
s를 그때그때 결과라고 하면, 오름차순 정렬에서 s가 m 이하인 경우를 찾는 것이기 때문에 s <= m 이면 답이 될 수 있고,
오른쪽을 찾아봐야하기 때문에 l = mid라고 생각할 수 있지만, 이것은 들어갈 곳이 아닌 한 점을 찾는 것이기 때문에
l <= r을 한 이상 안 끝날 수도 있다. 따라서 mid는 따로 기록해두고, l = mid + 1, r= mid - 1로 해가며 찾아야한다.
"""

View File

@@ -0,0 +1,40 @@
# 회전 초밥
import sys
from collections import Counter
input = sys.stdin.readline
def solution():
n, d, k, c = map(int, input().rstrip().split())
belt = [int(input().rstrip()) for _ in range(n)]
double_belt = belt + belt
eat = Counter(belt[:k])
eat.setdefault(c, 0)
eat[c] += 1
result = len(eat)
for i in range(1, len(double_belt)-k+1):
eat[double_belt[i-1]] -= 1
if eat[double_belt[i-1]] == 0:
del eat[double_belt[i-1]]
eat.setdefault(double_belt[i+k-1], 0)
eat[double_belt[i+k-1]] += 1
result = max(result, len(eat))
print(result)
return
solution()
"""
걸린 시간: 24분
시간 복잡도: 2n 길이의 리스트를 1번 순회하고, 그 과정에서 처리는 dict로 O(1)이므로, 전체 시간복잡도는 O(n)이다.
해설: 같은 두 리스트를 이어붙여서 원형을 만든 후 k 구간씩 슬라이딩하면서 다른 종류의 개수를 세면 된다.
각 음식마다 있는 개수는 dict에 저장하고, key의 개수 세기로 O(1)에 종류 개수를 세기 때문에 value가 0이면 del로 key까지 없앤다.
"""

View File

@@ -0,0 +1,29 @@
# 임스와 함께하는 미니게임
import sys
input = sys.stdin.readline
game_types = {"Y": 2, "F": 3, "O": 4}
def solution():
q = list(input().rstrip().split())
n, personnel = int(q[0]), game_types.get(q[1], 0)
people = set()
for _ in range(n):
people.add(input().rstrip())
result = len(people) // (personnel-1)
print(result)
return
solution()
"""
걸린 시간: 18분
시간 복잡도: 사람들 이름을 쭉 읽고, 쓰고, set() 개수 세는 것까지 n이므로 전체 시간복잡도는 O(n)
해설: 한 사람이 임스와 두 번 게임을 할 수는 없으므로 참여 명단을 중복을 제거하는 set()을 활용한다.
그 후 임스 본인을 제외한 게임에 필요한 인원 수로 set() 길이를 나누면 몫이 임스가 할 수 있는 게임 수이다.
"""

View File

@@ -0,0 +1,63 @@
# 비슷한 단어
import sys
from collections import Counter
input = sys.stdin.readline
def check_similar(now_word, target_c):
target_c_copy = target_c.copy()
now_word_left = []
for c in now_word:
if len(target_c_copy) < 1:
break
if target_c_copy.get(c, -1) == -1:
now_word_left.append(c)
continue
target_c_copy[c] -= 1
if target_c_copy[c] == 0:
del target_c_copy[c]
len_target_left = sum(target_c_copy.values())
result = 1 if len_target_left <= 1 and len(now_word_left) <= 1 else 0
return result
def solution():
n = int(input().rstrip())
result = 0
target = input().rstrip()
target_c = Counter(target)
for _ in range(n-1):
now_word = input().rstrip()
if len(now_word) >= len(target)+2:
continue
result += check_similar(now_word, target_c)
print(result)
return
solution()
"""
걸린 시간: 32분
시간 복잡도: 단어 길이를 m이라고 하자. 중복 제거할 때는 둘 중 짧은 단어만큼 진행된다. -> O(m)
target_c의 values 합 구하기 -> O(m)
이 두 과정이 check_similar()이고, O(m)이다. 이제 이 과정을 n-1개의 단어만큼 진행하기 때문에 전체 시간복잡도는 O(nm)이다.
해설: 비슷한 단어의 조건을 수치적으로 정리하면 target과 비교하는 단어의 길이 차이가 2이상 나면 안되고,
겹치는 글자들을 개수 기준으로 다 제거했을 때 양쪽 다 남은 글자가 1을 초과하면 안된다.
따라서 나는 target을 Counter로 만들고, 비교하는 단어를 쭉 보면서 target_c에 있으면 개수를 줄이는 방식으로 중복을 제거했다.
이때 target_c에 없으면 남는 글자는 따로 리스트에 보관하였다.
마지막으로 target_c의 values 합이 남는 글자 수고, len(lst)가 비교하는 글자의 남는 글자 수이므로 이것의 길이가 둘 다 <= 1일때만
비슷한 단어로 판정했다.
"""

View File

@@ -0,0 +1,63 @@
# KCPC
import sys
input = sys.stdin.readline
def write_board(board, i, j, s, idx):
if not board.get(i, 0):
board[i] = {"problems": {}, "submit_count": 0, "last_submit": -1}
board[i]["problems"][j] = max(board[i]["problems"].get(j, 0), s)
board[i]["submit_count"] += 1
board[i]["last_submit"] = idx
return
def check_our_rank(board, t):
ranking = []
for k in board:
info = (k, -sum(board[k]["problems"].values()), board[k]["submit_count"], board[k]["last_submit"])
ranking.append(info)
ranking.sort(key=lambda x: (x[1], x[2], x[3]))
for i, record in enumerate(ranking):
if record[0] == t:
return i+1
def test(n ,k ,t, m):
board = {} # 팀id: {문제: {번호: 점수}, 제출 횟수: ~, 마지막 제출 인덱스: ~}
for idx in range(m):
i, j, s = map(int, input().rstrip().split())
write_board(board, i, j, s, idx)
return check_our_rank(board, t)
def solution():
T = int(input().rstrip())
for _ in range(T):
n, k, t, m = map(int, input().rstrip().split())
print(test(n, k, t, m))
return
solution()
"""
걸린 시간: 40분
시간 복잡도: write_board가 O(1), check_our_rank가 sort때문에 O(nlogn)이므로, 이 둘을 활용한 test는 O(nlogn)이다.
따라서 전체 시간복잡도는 test하는 횟수를 곱해서 O(Tnlogn)이다.
해설: 문제에서 시키는대로 구현하면 된다. 순위를 정하기 위해 마지막에 정렬해야 하는데 기준이 되는 요소들이
최종 점수 > 제출 횟수 > 마지막 제출 시간이기 때문에 이 정보들을 dict에 기록하면서 진행하기로 했다.
최종 점수를 계속 갱신하는 것이 신경쓸 것이 좀 있어서 마지막에 한번에 모아서 계산하는거랑 그때그때 갱신하는 거랑 시간복잡도는 같다고
판단하여 마지막에 계산하였다.
그 후 팀 id와 3가지 요소를 튜플로 묶어서 리스트에 넣은 다음 lambda를 활용하여 sort 하였다.
동순위는 없으므로 팀 id가 t인 튜플의 인덱스+1을 한 것이 답이다.
"""

View File

@@ -0,0 +1,65 @@
# 비밀번호 발음하기
import sys
input = sys.stdin.readline
alphabet = "abcdefghijklmnopqrstuvwxyz"
VOWEL = "aeiou"
CONSONANT_OR_VOWEL = {a: 0 for a in alphabet} # 0이면 자음, 1이면 모음
for v in VOWEL:
CONSONANT_OR_VOWEL[v] = 1
def check_word(word):
is_vowel_in = False
sequence_count = [-1, 0] # sequence_count[0]은 자음(0),모음(1) 구분, sequence_count[1]은 count
before_w = ""
for w in word:
if not is_vowel_in and CONSONANT_OR_VOWEL[w] == 1: # 1번째 조건
is_vowel_in = True
if sequence_count[0] == CONSONANT_OR_VOWEL[w]: # 2번째 조건
sequence_count[1] += 1
if sequence_count[1] >= 3:
return False
else:
sequence_count[0], sequence_count[1] = CONSONANT_OR_VOWEL[w], 1
if before_w not in ("e", "o") and before_w == w: # 3번째 조건
return False
before_w = w
if not is_vowel_in: # 1번째 조건 최종 확인
return False
return True
def solution():
while 1:
word = input().rstrip()
if word == "end":
break
is_acceptable = check_word(word)
if is_acceptable:
print(f"<{word}> is acceptable.")
else:
print(f"<{word}> is not acceptable.")
return
solution()
"""
걸린 시간: 25분
시간 복잡도: 들어오는 모든 단어의 길이만큼의 시간이 걸린다.
해설: 자음과 모음 여부를 dictionary에 1, 0으로 O(1)에 구분할 수 있도록 세팅을 해둔다.
1번 조건은 한글자씩 볼 때 모음 여부에 따라 is_vowel_in 진위 여부를 결정한다.
2번 조건은 어떤 것이 몇 번 연속되고 있는지 현황을 기록하는 길이 2 리스트를 만들어서 판단한다. 기록과 다른 것이 나오면 초기화한다.
3번 조건은 이전 단어를 저장하는 변수를 만들고 이전 단어가 "e", "o"이면 통과하고, 아닐 경우 2번 연속되는지 확인한다.
"""

View File

@@ -0,0 +1,35 @@
# 올림픽
import sys
input = sys.stdin.readline
def solution():
n, k = map(int, input().rstrip().split())
records = [0] * n
for _ in range(n):
a, b, c, d = map(int, input().rstrip().split())
records[a-1] = (b,c,d)
ordered_records = sorted(records, key=lambda x:(-x[0], -x[1], -x[2]))
target = records[k-1]
rank = 0
while 1:
now = (ordered_records[rank][0], ordered_records[rank][1], ordered_records[rank][2])
if now == target:
break
rank += 1
print(rank+1)
return
solution()
"""
걸린 시간: 40분(dictionary 써서 더 좋게 해보려다가 늦음..)
시간 복잡도: nlogn(정렬) + kn(기록, 탐색) => O(nlogn)
해설: k를 인덱스에 맞춰서 records에 메달만 튜플로 넣는다.
records를 메달 규칙에 맞게 정렬 후 while로 타켓 튜플을 처음 찾고 +1 하면 그것이 그 그룹의 등수이다.
"""

View File

@@ -0,0 +1,79 @@
# 크로스 컨트리
import sys
from collections import Counter
input = sys.stdin.readline
def exclude_team(teams):
excluded = set()
for team in teams:
if teams[team] != 6:
excluded.add(team)
return excluded
def find_winning_team(rank, excluded, team_count, n):
team_score = {i: {"count": 0, "score": 0} for i in range(1, team_count+1) if i not in excluded}
first_5th = {}
now_rank = 0
for r in rank:
if r in excluded:
continue
now_rank += 1
if team_score[r]["count"] >= 4:
if first_5th.get(r, 0) == 0:
first_5th[r] = now_rank
continue
team_score[r]["score"] += (now_rank)
team_score[r]["count"] += 1
winner = {"team": 0, "score": float("inf"), "5th": n+1}
for team in team_score:
if team_score[team]["score"] < winner["score"]:
winner["team"], winner["score"], winner["5th"] = team, team_score[team]["score"], first_5th.get(team, n+1)
elif team_score[team]["score"] == winner["score"] and first_5th.get(team, n+1) < winner["5th"]:
winner["team"], winner["score"], winner["5th"] = team, team_score[team]["score"], first_5th.get(team, n+1)
else:
continue
return winner["team"]
def game():
n = int(input().rstrip())
rank = list(map(int, input().rstrip().split()))
teams = Counter(rank)
excluded = exclude_team(teams)
result = find_winning_team(rank, excluded, len(teams), n)
return result
def solution():
t = int(input().rstrip())
for _ in range(t):
result = game()
print(result)
return
solution()
"""
걸린 시간: 50분 정도(score를 반대로 생각하거나, dictionary keyerror 때문에..) -> dict는 앵간하면 .get()으로 하자.
시간 복잡도: exclude_team()은 Counter 객체 전체를 순회하기 때문에 O(m)이다.
find_winning_team()은 rank 전체를 순회하고 팀 리스트를 순회하는 것인데 set과 dict를 쓰므로 O(1)이라서 전체는 m < n이므로 O(n)이다.
game()은 Counter이므로 O(m)에다가 exclude_team()과 find_winning_team()을 돌리기 때문에 전체는 O(n)이다.
마지막으로 전체 프로그램은 game()을 t번 반복하기 때문에 전체 시간복잡도는 O(t*n)이다.
함수별로 나눠놓으니까 전체 시간복잡도를 계산하는 것이 확실히 편하다.
해설: 먼저 6명이 아닌 팀을 counter로 알아낸 뒤 제외한다. 그 후 rank 리스트를 순차적으로 확인하면서 팀의 총점을 기록한다.
이때, 4등까지만 계산을 하도록 조건을 걸어주고, 팀의 5등이 전체 몇 등인지만 dict에 저장한다.
마지막에 총점을 비교하고, 같다면 5등까지 확인하는 절차를 거쳐서 최종 우승 팀을 결정한다.
"""

View File

@@ -0,0 +1,38 @@
# 돌 게임
import sys
input = sys.stdin.readline
def solution():
n = int(input().rstrip())
print("SK") if n%2 == 1 else print("CY")
return
solution()
"""
걸린 시간: 25분(dp로도 풀 수 있을 것 같아서 고민하다가..)
시간 복잡도: 그냥 % 계산 한번이니까 O(1)이다.
해설: 마지막에 무조건 창영이가 가져가기 때문에 상근이가 이기려면 돌을 남겨두면 안된다.
완벽하게 게임을 한다는 이야기는 서로 이기기 위해 최선을 다한 수만 생각한다.
k번째에 경기가 끝난다고 할 때, 상근이가 이기려면 k-1번째에 경기가 끝났을 때 1, 3개 중에 남아야 하고, 0, 2개가 남으면 창영이가 이긴다.
한 경기가 끝나면 2 or 4 or 6개의 돌이 없어지므로, 무조건 짝수개씩 사라지기 때문에 n이 짝수면 홀수개 즉, 1,3개가 절대 남을 수 없다.
반대도 마찬가지이므로 n이 짝수면 창영이의 승리, n이 홀수면 상근이의 승리이다.
dp로 만들어보면 dp[i]는 i개의 돌이 있을 때 상근이의 승리 여부이다. (dp[i]=1 -> 상근이 승리)
dp[i-1]에서 상근이가 이겼다면 1개 가져와서 이긴 경우, dp[i]는 1개가 남으므로 창영이 승리
dp[i-1]에서 상근이가 이겼다면 3개 가져와서 이긴 경우, dp[i]는 4개가 남으므로 어떻게 해도 창영이 승리
dp[i-1]에서 상근이가 진 상황도 위와 그대로 반대이다. 따라서 dp[i] = 1-dp[i-1]
초기 dp는 0번째만 필요하므로 dp[0]=1 (돌이 1개이고, 상근이가 시작하니까)
dp[i]의 적절한 상황을 구성하고, i상황이 되기 위해 연관되는 i 이전의 상황들을 상정하여 dp 식을 세우면 된다.
이번 상황은 i-1번째 상황만으로 i번째 상황이 결정된다.
"""