Coding Test/BaekJoon_Python
백준 11377 <열혈강호 3> Python
JunOnJuly
2024. 10. 6. 18:42
728x90
https://www.acmicpc.net/problem/11377
열혈강호 2 까지는 이분매칭으로 풀었지만 해당 문제는 네트워크 플로우 알고리즘 중 에드몬드 카프 알고리즘으로 풀었습니다.
아마 2 개의 일을 할 수 있는 K명을 정하는데 큰 지장이 있을 것이라고 생각합니다.
모두 두 개씩 일을 할 수 있을 때 (열혈강호 2) 이분 매칭을 두 번 돌렸던 것을 생각해 우선 모두에게 일을 하나씩 되는대로 할당하고 추가적으로 일을 할당하는 방식으로 진행했습니다.
문제에서 제시한 예제를 그래프로 만들면 이렇게 됩니다.
해당 그래프에서 이동하는 경로는
0 - 1 - 6 - 12
0 - 2 - 7 - 12
0 - 3 - 10 - 12
0 - 11 - 1 - 8 - 12
으로 우선 모든 직원들에게 일을 할당한 뒤 추가 노드에서 일을 다시 분배함을 볼 수 있습니다.
하지만 사실 어떤 노드를 먼저 순회해도 상관은 없습니다.
import sys
from collections import deque, defaultdict
input = sys.stdin.readline
def solution(N, M, K, hope_list):
# 흐를 수 있는 유량
# flowable[i] = [[i 와 연결된 노드, 흐를 수 있는 유량], ...]
# 0 : 소스 / 1 ~ N : 직원 / N+1 ~ N+M : 일 / N+M+1 : 추가 인력 노드 / N+M+2 : 싱크
flowable = defaultdict(lambda : defaultdict(int))
## 소스 -> 직원 -> 일 -> 싱크
# 소스에서 직원에게 1 씩 일을 할당 가능
for i in range(1, N+1):
flowable[0][i] = 1
# 직원은 하고싶은 일을 선택 가능
for i in range(len(hope_list)):
for j in range(len(hope_list[i])):
flowable[i+1][hope_list[i][j]+N+1] = 1
# 일은 모두 싱크로 흘러감
for i in range(N+1, N+M+1):
flowable[i][N+M+2] = 1
## 소스 -> 추가 인력 -> 직원
# 소스에서 추가 인력노드로 추가인력 모두 할당 가능
flowable[0][N+M+1] = K
# 추가 인력 노드는 모든 직원에게 인력 충원 가능
for i in range(1, N+1):
flowable[N+M+1][i] = 1
# 현재 노드에 있는 유량
fluid = [N+K] + [0 for _ in range(N+M+2)]
# 네트워크 플로우
# 싱크에 도달할 수 없을 때 까지
while True:
# 데크
dq = deque([0])
# 방문목록
# visited[i] = 직전노드
visited = [-1 for _ in range(N+M+3)]
visited[0] = 0
# BFS
while dq:
# 싱크에 도달했으면
if dq[-1] == N+M+2:
# 종료
break
# 현재 노드
now_node = dq.popleft()
# 이동 가능한 노드 순회
for node, flow in flowable[now_node].items():
# 방문하지 않았고 흐를 수 있는 유량이 존재하면
if visited[node] < 0 and flow:
# 큐에 삽입
dq.append(node)
# 방문 체크
visited[node] = now_node
# 데크가 비어있다면 끝
if not dq:
break
# 방문목록을 역순회해 경로 추적 및 흐를 수 있는 유체량 추적
# 경로
route = deque([N+M+2])
# 흐를 수 있는 유체의 최대량
max_flow = float('inf')
# 경로 추적
now_node = N+M+2
while now_node != 0:
# 직전 노드
before_node = visited[now_node]
# 경로에 추가
route.appendleft(before_node)
# 유체의 최대량 업데이트
max_flow = min(max_flow, flowable[before_node][now_node])
# 노드 최신화
now_node = before_node
# 경로를 따라 유체 흘려주기
for i in range(len(route)-1):
flowable[route[i]][route[i+1]] -= max_flow
# 반대로도 흘려주기
flowable[route[i+1]][route[i]] += max_flow
# 유체량 업데이트
fluid[route[i]] -= max_flow
fluid[route[i+1]] += max_flow
# 싱크에 있는 유량 리턴
print(fluid[-1])
# 입력
N, M, K = map(int, input().strip().split())
hope_list = [list(map(lambda x: int(x)-1, input().strip().split()))[1:] for n in range(N)]
solution(N, M, K, hope_list)
728x90