이 포스트는 프로그래밍 문제 사이트 백준 Online Judge의 3640번 : '제독' 문제의 풀이 코드와 해설을 다루고 있습니다.
문제 난이도는 Solved.ac 기준 Platinum II에 해당하며, 문제를 풀이하기 위해 최소 비용 최대 유량 알고리즘(MCMF)에 대한 이해가 필요합니다.
3640번 : 제독
3640번: 제독
두 함선(빨강, 파랑)은 1에서 시작해서 6에서 만난다. 빨간 함선은 1 → 3 → 6 (총 33개 포탄)으로 이동하고, 파란 함선은 1 → 2 → 5 → 4 → 6 (총 53개 포탄)으로 이동한다. 두 경로에서 출발
www.acmicpc.net
시작 노드와 도착 노드만 동일하고 나머지 노드는 겹치지 않는 두 경로를 잡을 때, 두 경로의 비용이 최소가 되는 합을 구하는 문제입니다.
경로의 수가 2개이기 때문에, 시작 노드와 마지막 노드에는 Capacity를 2로 설정하고, 모든 노드들을 분할하여 in 노드와 out 노드 사이의 Capacity를 1로 잡아주면, 두 경로는 같은 노드를 중복하여 방문할 수 없도록 장치가 마련되므로 답을 구할 수 있습니다.
이 때 최소 비용을 구하는 것은 MCMF 알고리즘을 활용해서 계산해줍니다.
#include <cstdio>
#include <vector>
#include <queue>
#include <algorithm>
#define MAX 2005
#define INF 1000000000
using namespace std;
int Capacity[MAX][MAX], Flow[MAX][MAX], SinCost[MAX][MAX];
vector<int> Line[MAX];
void AddLine(int From, int To, int Cap, int Cost) {
Line[From].push_back(To), Line[To].push_back(From);
Capacity[From][To] = Cap;
SinCost[From][To] = Cost, SinCost[To][From] = -Cost;
}
int MCMF(int Sour, int Sink) {
int CostSum = 0;
while(true) {
int Prev[MAX], Cost[MAX];
fill(Prev, Prev+MAX, -1);
fill(Cost, Cost+MAX, INF);
Cost[Sour] = 0;
queue<int> Queue;
Queue.push(Sour);
bool isInQueue[MAX];
fill(isInQueue, isInQueue+MAX, false);
isInQueue[Sour] = true;
while(!Queue.empty()) {
int Curr = Queue.front();
Queue.pop();
isInQueue[Curr] = false;
for(int i=0; i<Line[Curr].size(); i++) {
int Next = Line[Curr][i];
if(Capacity[Curr][Next] - Flow[Curr][Next] > 0
&& Cost[Curr] + SinCost[Curr][Next] < Cost[Next]) {
Cost[Next] = Cost[Curr] + SinCost[Curr][Next];
Prev[Next] = Curr;
if(!isInQueue[Next]) {
Queue.push(Next);
isInQueue[Next] = true;
}
}
}
}
if(Prev[Sink] == -1) break;
for(int i=Sink; i!=Sour; i=Prev[i]) {
CostSum += SinCost[Prev[i]][i];
Flow[Prev[i]][i]++;
Flow[i][Prev[i]]--;
}
}
return CostSum;
}
int main() {
int N, M, From, To, Cost;
while(scanf("%d", &N) != EOF) {
scanf("%d", &M);
fill(&Capacity[0][0], &Capacity[MAX-1][MAX], 0);
fill(&Flow[0][0], &Flow[MAX-1][MAX], 0);
fill(&SinCost[0][0], &SinCost[MAX-1][MAX], INF);
for(int i=0; i<MAX; i++) vector<int>().swap(Line[i]);
int Sour = N*2+2, Sink = N*2+3;
AddLine(Sour, 1*2, 2, 0), AddLine(N*2+1, Sink, 2, 0);
AddLine(1*2, 1*2+1, 2, 0), AddLine(N*2, N*2+1, 2, 0);
for(int i=2; i<N; i++) AddLine(i*2, i*2+1, 1, 0);
for(int i=0; i<M; i++) {
scanf("%d %d %d", &From, &To, &Cost);
AddLine(From*2+1, To*2, 1, Cost);
}
printf("%d\n", MCMF(Sour, Sink));
}
}
풀이 코드는 위와 같습니다.
입력부에 EOF를 처리하는 것과 매 테스트케이스마다 배열과 벡터를 초기화해주는 과정, 그리고 시작 노드와 끝 노드의 Capacity를 2로 설정해주는 과정을 제외하고는 일반적인 MCMF 문제와 동일합니다.
제출해보면 188ms의 시간이 소요되고 모든 테스트케이스를 통과할 수 있습니다.
'알고리즘 > 백준(BOJ) 문제풀이' 카테고리의 다른 글
[백준/BOJ C++] 10786번 : Catering (ACM-ICPC World Finals 2015 C번) (0) | 2021.12.31 |
---|---|
[백준/BOJ C++] 3938번 : Concert Hall Scheduling (최소 비용 최대 유량) (0) | 2021.12.30 |
[백준/BOJ C++] 2311번 : 왕복 여행 (최소 비용 최대 유량 알고리즘, MCMF) (0) | 2021.12.29 |
[백준/BOJ C++] 1658번 : 돼지 잡기 (최대 유량, Network Flow) (1) | 2021.12.29 |
[백준/BOJ C++] 1420번 : 학교 가지마! (최대 유량 최소 컷 정리, 맵의 연결리스트 구현) (0) | 2021.12.29 |