Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 92 additions & 0 deletions INSEA-99/week01/week10/10026_적록색약.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# pypy3
# 시간(ms) : 152
# 공간(KB) : 115588
#
# 공유 :
# - 정상인과 적록색약이 겹치는 구역이 있어 동시에 탐색하는게 좋지 않을까 생각
# - 함수는 한가지 기능만 구현하는게 좋은데, 위 방식은 2가지를 한 번에 탐색하려해서 코드 복잡도가 올라감
# - 오히려 코드 리뷰/유지보수 측면에서 안좋고 디버깅하기 어려움
# - 결론 : 따로 탐색하는게 낫다. 미세한 성능차이를 얻으려다 많은 것을 잃는다약간의

import sys
from collections import deque
input = sys.stdin.readline
# 방향: ↑, ↓, ←, →
dr = [-1, 1, 0, 0]
dc = [0, 0, -1, 1]

def bfs(r, c, is_normal, is_colorblind):
"""
정상인과 적록색약인 사람의 구역을 동시에 탐색하는 BFS
:param r, c: 시작 좌표
:param is_normal: 정상인 시각으로 탐색할지 여부
:param is_colorblind: 적록색약 시각으로 탐색할지 여부
"""
global n
dq = deque([(r, c, is_normal, is_colorblind)])

while dq:
r, c, is_normal, is_colorblind = dq.popleft() # BFS는 FIFO

# 상하좌우 4방향 탐색
for d in range(4):
nr, nc = r + dr[d], c + dc[d]

# 범위 체크: 벗어나면 스킵
if not (0 <= nr < n and 0 <= nc < n):
continue

n_is_normal = n_is_colorblind = False

# 정상인 시각: 같은 색(R, G, B)끼리만 연결
if is_normal and not normal_visited[nr][nc] and area[nr][nc] == area[r][c]:
normal_visited[nr][nc] = True
n_is_normal = True

# 적록색약 시각: R과 G는 같은 색으로 취급, B는 따로
if is_colorblind and not colorblind_visited[nr][nc]:
# (현재와 다음이 모두 R 또는 G) 또는 (같은 색)이면 연결
if (area[r][c] in 'RG' and area[nr][nc] in 'RG') or area[r][c] == area[nr][nc]:
colorblind_visited[nr][nc] = True
n_is_colorblind = True

# 하나라도 방문했으면 큐에 추가
if n_is_normal or n_is_colorblind:
dq.append((nr, nc, n_is_normal, n_is_colorblind))


# 입력
n = int(input())
area = [list(input().strip()) for _ in range(n)]

# 구역 개수 카운트
normal_area = 0
colorblind_area = 0

# 방문 체크 배열
normal_visited = [[False] * n for _ in range(n)]
colorblind_visited = [[False] * n for _ in range(n)]

# 모든 칸을 순회하며 새로운 구역 발견 시 BFS 시작
for r in range(n):
for c in range(n):
is_normal = False
is_colorblind = False

# 정상인 시각에서 미방문 구역이면 새 구역 시작
if not normal_visited[r][c]:
normal_visited[r][c] = True
is_normal = True
normal_area += 1

# 적록색약 시각에서 미방문 구역이면 새 구역 시작
if not colorblind_visited[r][c]:
colorblind_visited[r][c] = True
is_colorblind = True
colorblind_area += 1

# 하나라도 새 구역이면 BFS로 연결된 칸 모두 방문 처리
if is_normal or is_colorblind:
bfs(r, c, is_normal, is_colorblind)

print(normal_area, colorblind_area)
42 changes: 42 additions & 0 deletions INSEA-99/week01/week10/1967_트리의_지름.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# pypy3
# 시간(ms) : 116
# 공간(KB) : 245360
#
# 공유 :
# - 예시가 이진트리라서 이진트리인줄 알았는데 그런 언급은 없어서 3개 이상의 자식 노드 고려 필요했음

import sys
input = sys.stdin.readline
sys.setrecursionlimit(10**5)

def dfs(root):
"""현재 노드에서 가장 긴 경로의 길이를 반환"""
global ans
visited[root] = True
dist = [0, 0] # 가장 긴 두 경로를 저장
for neighbor, weight in adj[root]:
if not visited[neighbor]:
maintain_top_two(dist, dfs(neighbor) + weight) # 자식 노드로부터 가장 긴 경로 + 간선 가중치

ans = max(ans, dist[0] + dist[1]) # 가장 긴 두 경로를 합쳐서 지름 후보 갱신
return dist[0]

def maintain_top_two(list, n):
"""리스트에서 가장 큰 두 값을 유지"""
if n > list[0]:
list[1] = list[0]
list[0] = n
elif n > list[1]:
list[1] = n

n = int(input())
adj = [[] for _ in range(n+1)]
visited = [False] * (n+1)
for _ in range(n-1):
u, v, w = map(int, input().split())
adj[u].append((v, w))
adj[v].append((u, w))

ans = 0
dfs(1)
print(ans)
72 changes: 72 additions & 0 deletions INSEA-99/week01/week10/2580_스도쿠.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# pypy3
# 시간(ms) : 540
# 공간(KB) : 241236

import sys
input = sys.stdin.readline
sys.setrecursionlimit(10**5)

def backtrack(depth):
"""depth번째 빈 칸을 채우는 백트래킹 함수"""
if depth == total_blanks: # 모든 빈 칸을 채웠으면
return True

row_idx, col_idx = blank_positions[depth] # 튜플 언패킹으로 가독성 향상
box_idx = box_number[row_idx][col_idx] # 반복 계산 제거

for num in range(9): # 1~9까지 시도 (인덱스 0~8)
# 같은 행, 열, 3x3 박스에 num+1이 없는지 한 번에 체크 (is_valid 함수 인라인화)
if not (used_in_row[row_idx][num] or used_in_col[col_idx][num] or used_in_box[box_idx][num]):
# 숫자 배치
board[row_idx][col_idx] = num + 1
used_in_row[row_idx][num] = 1
used_in_col[col_idx][num] = 1
used_in_box[box_idx][num] = 1

if backtrack(depth + 1): # 다음 빈 칸으로 이동
return True

# 백트래킹: 숫자 제거
board[row_idx][col_idx] = 0
used_in_row[row_idx][num] = 0
used_in_col[col_idx][num] = 0
used_in_box[box_idx][num] = 0

return False


# 입력 받기: 9x9 스도쿠 보드
board = []
for _ in range(9):
board.append(list(map(int, input().split())))

# 각 칸이 속한 3x3 박스 번호 (0~8)
# 0 1 2
# 3 4 5
# 6 7 8
box_number = [[0,0,0,1,1,1,2,2,2] for _ in range(3)] + \
[[3,3,3,4,4,4,5,5,5] for _ in range(3)] + \
[[6,6,6,7,7,7,8,8,8] for _ in range(3)]

blank_positions = [] # 빈 칸(0인 칸)의 위치 저장
total_blanks = 0 # 빈 칸의 총 개수

# 각 행, 열, 3x3 박스에서 1~9 숫자 사용 여부 체크 (인덱스 0~8이 1~9를 의미)
used_in_row = [[0] * 9 for _ in range(9)] # used_in_row[i][n]: i행에 n+1이 있으면 1
used_in_col = [[0] * 9 for _ in range(9)] # used_in_col[j][n]: j열에 n+1이 있으면 1
used_in_box = [[0] * 9 for _ in range(9)] # used_in_box[b][n]: b번 박스에 n+1이 있으면 1

# 초기 보드 분석: 빈 칸 찾기 및 사용된 숫자 체크
for i in range(9):
for j in range(9):
if board[i][j] == 0: # 빈 칸 발견
blank_positions.append([i, j])
total_blanks += 1
else: # 이미 채워진 칸
num = board[i][j] - 1 # 1~9를 0~8 인덱스로 변환
used_in_row[i][num] = 1
used_in_col[j][num] = 1
used_in_box[box_number[i][j]][num] = 1

backtrack(0) # 0번째 빈 칸부터 시작
for row in board: print(*row)
44 changes: 44 additions & 0 deletions INSEA-99/week01/week10/9663_N-Queen.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# pypy3
# 시간(ms) : 4760
# 공간(KB) : 245896

import sys
input = sys.stdin.readline
sys.setrecursionlimit(10**6)

def backtrack(row):
"""row번째 행에 퀸을 배치하는 백트래킹 함수"""
if row == N + 1: # 모든 행에 퀸을 배치했으면
count[0] += 1
else:
for col in range(1, N + 1): # 각 열에 퀸을 배치 시도
if used_col[col] == 1: continue # 이미 해당 열에 퀸이 있으면 스킵
if is_safe(row, col): # 대각선 체크
# 퀸 배치
used_col[col] = 1
diag_sum[row + col] = 1 # 우상향 대각선 (row+col 값이 같음)
diag_sub[row - col] = 1 # 좌하향 대각선 (row-col 값이 같음)

backtrack(row + 1) # 다음 행으로 이동

# 백트래킹: 퀸 제거
used_col[col] = 0
diag_sum[row + col] = 0
diag_sub[row - col] = 0

def is_safe(row, col):
"""(row, col) 위치에 퀸을 놓을 수 있는지 대각선 체크"""
if diag_sum[row + col] == 1 or diag_sub[row - col] == 1:
return False
return True

# 변수 초기화
count = [0] # 경우의 수 (리스트로 선언하여 함수 내에서 수정 가능)
N = int(input()) # 체스판 크기
used_col = [0] * (N + 1) # 각 열에 퀸이 있는지 체크
diag_sum = [0] * (N * 2 + 1) # 우상향 대각선 체크 (row+col 인덱스)
diag_sub = [0] * (N * 2 - 1) # 좌하향 대각선 체크 (row-col 인덱스)

backtrack(1) # 1번 행부터 시작
print(count[0])