코딩/백준-알고리즘
[백준] 12670 The Year of Code Jam (Large) Python 3
안녕 나의 20대
2025. 3. 4. 14:25
반응형
import sys
from collections import deque
class Dinic:
def __init__(self, n):
"""
n: 전체 노드 수
인접 리스트 구조:
adj[u] = [(v, capacity, rev), ...]
- v: 다음 노드
- capacity: 잔여 용량
- rev: v의 인접 리스트에서 역방향 간선의 index
"""
self.n = n
self.adj = [[] for _ in range(n)]
def add_edge(self, u, v, cap, is_directed=True):
"""
u -> v 용량 cap인 간선 추가.
is_directed=False 이면 u <-> v 모두 용량 cap.
"""
# 정방향 간선(u->v)
self.adj[u].append([v, cap, len(self.adj[v])])
# 역방향 간선(v->u)
rev_cap = 0 if is_directed else cap
self.adj[v].append([u, rev_cap, len(self.adj[u]) - 1])
def bfs(self, s, t, level):
"""
Dinic용 레벨 그래프 구성(BFS).
level[u] = s에서 u까지의 최단거리(간선 수)
t까지 도달하면 True, 아니면 False
"""
for i in range(len(level)):
level[i] = -1
queue = deque()
queue.append(s)
level[s] = 0
while queue:
u = queue.popleft()
for v, cap, _ in self.adj[u]:
if cap > 0 and level[v] == -1: # 잔여 용량 > 0, 방문 안 한 노드
level[v] = level[u] + 1
queue.append(v)
return level[t] != -1
def send_flow(self, u, flow, t, level, it):
"""
Dinic용 DFS (Blocking Flow를 찾음).
- u: 현재 노드
- flow: 현재까지 흘릴 수 있는 유량
- t: 싱크 노드
- level: 레벨 그래프
- it[u]: u에서 탐색을 시작할 간선 인덱스 (중복 탐색 방지)
"""
if u == t:
return flow
while it[u] < len(self.adj[u]):
v, cap, rev = self.adj[u][it[u]]
if cap > 0 and level[v] == level[u] + 1:
# 다음 노드로 보낼 수 있는 잔여 용량만큼 보내보기
cur_flow = min(flow, cap)
temp_flow = self.send_flow(v, cur_flow, t, level, it)
if temp_flow > 0:
# 간선 용량 갱신
self.adj[u][it[u]][1] -= temp_flow # 정방향 잔여 용량 감소
self.adj[v][rev][1] += temp_flow # 역방향 잔여 용량 증가
return temp_flow
it[u] += 1
return 0
def max_flow(self, s, t):
"""
Dinic 알고리즘으로 s->t 최대 유량 계산
"""
if s == t:
return -1
total = 0
level = [-1] * self.n
# 레벨 그래프가 더 이상 s에서 t로의 경로를 만들지 못할 때까지 반복
while self.bfs(s, t, level):
# it[u]: u에서 탐색해야 할 간선의 시작 인덱스 (중복 DFS 방지)
it = [0] * self.n
while True:
flow = self.send_flow(s, float('inf'), t, level, it)
if flow <= 0:
break
total += flow
return total
def solve_one_case(n, m, grid):
"""
문제에서 주어진 로직대로 그래프 구성 후 Dinic으로 최대 유량 계산
n, m: 그리드 크기
grid: '.' / '#' / '?' 등이 섞인 2차원 문자 리스트
"""
s = n * m
t = n * m + 1
mf = Dinic(n * m + 2)
for i in range(n):
for j in range(m):
idx = i*m + j
# (i>0) → (i-1, j) <-> (i, j)
if i > 0:
mf.add_edge(idx - m, idx, 1, is_directed=False)
# (j>0) → (i, j-1) <-> (i, j)
if j > 0:
mf.add_edge(idx - 1, idx, 1, is_directed=False)
if grid[i][j] == '?':
continue
# ('.') ^ ((i+j)&1) → s->node, 아니라면 node->t
if ((grid[i][j] == '.') ^ ((i+j) & 1)):
mf.add_edge(s, idx, 4)
else:
mf.add_edge(idx, t, 4)
max_f = mf.max_flow(s, t)
return 2*n*m - n - m - max_f
def main():
input = sys.stdin.readline
T = int(input().strip())
for tc_num in range(1, T + 1):
n, m = map(int, input().split())
# C++ 코드에서 (n+2)x(m+2)에 '.'으로 패딩
padded_grid = [['.' for _ in range(m + 2)] for _ in range(n + 2)]
for i in range(n):
row_str = input().rstrip()
for j in range(m):
padded_grid[i+1][j+1] = row_str[j]
# 문제에서 n+2, m+2로 패딩된 그리드를 사용
ans = solve_one_case(n + 2, m + 2, padded_grid)
print(f"Case #{tc_num}: {ans}")
if __name__ == '__main__':
main()
반응형