728x90
https://www.acmicpc.net/problem/11408
열혈강호 시리즈 다섯번째 문제입니다.
이번 문제 또한 최대 유량 문제이지만 그 중에서 최소 코스트를 구하는 최소 비용 최대 유량(MCMF) 문제 입니다. 거창한 이름과는 다르게 해결 방법은 크게 새롭지 않습니다.
최대 유량 문제에서 길을 탐색할 때 모든 엣지의 코스트가 1 이었기 때문에 BFS 를 사용해 단순히 경로만 파악했다면 코스트가 생겼기 때문에 최단 코스트가 발생하는 경로를 찾아주면 됩니다.
최단 경로를 구한다면 가장 간단하게 다익스트라 알고리즘을 생각할 수 있지만 최대 유량 문제에서 코스트를 계산할 때에는 음의 경로로 이동할 경우 음의 코스트를 더해주어야 하기 때문에 다익스트라는 사용할 수 없습니다. 때문에 음의 경로가 존재해도 경로를 파악할 수 있는 벨만-포드 알고리즘을 사용해야 합니다. 다만 벨만포드에 다익스트라를 합친 것 같은 알고리즘인 SPFA 알고리즘을 사용해 조금 더 빠르게 경로를 찾을 수 있습니다.
경로를 찾는 방식만 다를 뿐 다른 과정은 모두 동일하게 진행해주면 됩니다.
import sys
from collections import deque
input = sys.stdin.readline
# SPFA
def spfa(N, M, graph, flowable):
# 노드 수
V = N+M+2
# 직전 노드
visited = [-1 for _ in range(V)]
visited[0] = 0
# 거리가 갱신된 노드들
dq = deque([0])
# 데크에 노드가 들어간 수
in_dq = [0 for _ in range(V)]
in_dq[0] = 1
# 최단거리
min_dists = [float('inf') for _ in range(V)]
min_dists[0] = 0
# 순회
while dq:
# 현재 노드
now_node = dq.popleft()
# 현재 노드가 V 번 이상 데크에 들어갔으면
if in_dq[now_node] >= V:
# 음의 순환이 생기면 중단
break
# 현재 노드와 연결된 노드 순회
for next_node, dist in graph[now_node]:
# 연결된 노드에 흐를수 있는 유체의 양이 남아있으면
if flowable[now_node][next_node]:
# 다음 노드까지의 거리
next_dist = min_dists[now_node] + dist
# 최단 거리보다 짧으면
if next_dist < min_dists[next_node]:
# 최단 거리 갱신
min_dists[next_node] = next_dist
# 직전 노드 갱신
visited[next_node] = now_node
# 데크에 들어가 있지 않으면
if next_node not in dq:
# 데크에 삽입
dq.append(next_node)
# 들어간 횟수 추가
in_dq[next_node] += 1
# 마지막에 도달하지 못했으면
if min_dists[-1] == float('inf'):
return [], -1
# 최단 경로
route = deque([V-1])
while route[0] != 0:
route.appendleft(visited[route[0]])
return route, min_dists[-1]
def solution(N, M, edges):
# 노드의 수
# 소스 + 노드들 + 싱크
V = N + M + 2
# 엣지 정리
edges = [[[edges[i][j]+N, edges[i][j+1]] for j in range(0, len(edges[i]), 2)] for i in range(len(edges))]
# 그래프
graph = [[] for _ in range(V)]
# 소스 -> 직원
for i in range(1, N+1):
graph[0].append([i, 0])
graph[i].append([0, 0])
# 직원 -> 일
for i in range(len(edges)):
for j, c in edges[i]:
graph[1+i].append([j, c])
graph[j].append([1+i, -c])
# 일 -> 싱크
for i in range(1+N, 1+N+M):
graph[i].append([V-1, 0])
graph[V-1].append([i, 0])
# 흐를수 있는 유체의 양
flowable = [[0 for _ in range(V)] for __ in range(V)]
for i in range(len(graph)):
for j, c in graph[i]:
# 정방향일때만
if i < j:
flowable[i][j] = 1
# 총 코스트
cost_sum = 0
# 할 수 있는 일
work_sum = 0
# 마지막에 도달할 수 없을 때 까지
while True:
# 코스트가 최소인 경로, 코스트
route, cost = spfa(N, M, graph, flowable)
# 도달할 수 없으면
if not route:
# 끝
break
# 경로를 따라 유체 이동
for i in range(len(route)-1):
flowable[route[i]][route[i+1]] -= 1
flowable[route[i+1]][route[i]] += 1
# 코스트 합
cost_sum += cost
work_sum += 1
print(work_sum)
print(cost_sum)
# 입력
N, M = map(int, input().strip().split())
edges = [list(map(int, input().strip().split()[1:])) for _ in range(N)]
solution(N, M, edges)
728x90
'Coding Test > BaekJoon_Python' 카테고리의 다른 글
백준 11405 <책 구매하기> Python (1) | 2024.10.30 |
---|---|
백준 14286 <간선 끊어가기 2> Python (1) | 2024.10.29 |
백준 2051 <최소 버텍스 커버> Python (0) | 2024.10.21 |
백준 2414 <게시판 구멍 막기> Python (1) | 2024.10.19 |
백준 3713 <당일치기> Python (0) | 2024.10.18 |