레야몬
[C++] 3640번 제독 - 최대 유량, 최소 비용 최대 유량 본문
1. 문제
- 한 중간 지점에서 두 전함이 겹치지 않는 뱃길과 중간지점을 택해 목적지에 도달한다. 중간 지점과 중간 지점 사이에는 사용해야 하는 포탄의 수가 정해져 있을 때 사용해야 하는 포탄의 최소 수를 구하시오.
2. 재정의
- 서로 겹치지 않는 두 경로의 가중치 합의 최솟값을 구하시오.
3. 해결방법
- 용량을 2로 설정하면 두 개의 경로가 만들어 지며 bfs로 최단 경로를 탐색하면 된다.
#include <iostream>
#include <string.h>
#include <vector>
#include <queue>
#define pii pair<int, int>
#define fi first
using namespace std;
const int INF = 987654321;
const int MAX_V = 2003;
const int OUT = 1000;
//간선 구조체
struct Edge {
//next로 가는 용량이 capacity이고 유량이 flow만큼 흐르며 가중치가 cost인 간선
int next, capacity, flow=0, cost;
Edge* rev;
Edge(){};
Edge(int _next, int _capacity, int _cost) : next(_next), capacity(_capacity), cost(_cost) {};
//흐를 수 있는 유량
int Remain() {
return capacity - flow;
}
//유량 흘려주기
void Push(int x) {
flow += x;
rev->flow -= x;
}
};
//방향 그래프
vector<Edge*> adj[MAX_V];
//그래프 간선 생성하기
void makeEdge(int u, int v, int capa, int c) {
Edge *uv = new Edge(v, capa, c), *vu = new Edge(u, 0, -c);
uv->rev = vu, vu->rev = uv;
adj[u].push_back(uv);
adj[v].push_back(vu);
}
int S, E;
//중간 지점의 수 v, 뱃길의 수 e
int v, e;
void input() {
cin >> v >> e;
if(cin.eof()) exit(0);
for(int i=0; i<MAX_V; i++)
adj[i].clear();
S = 1; E = v;
//2개의 경로가 존재
makeEdge(1, 1+OUT, 2, 0);
makeEdge(E, E+OUT, 2, 0);
//정점 분리
for(int i=2; i<E; i++)
makeEdge(i, i+OUT, 1, 0);
//뱃길에 맞은 간선 만들어주기
for(int i=0; i<e; i++) {
int a, b, c;
cin >> a >> b >> c;
makeEdge(a+OUT, b, 1, c);
}
}
int MCMF() {
//최소 비용
int minCost = 0;
while(1) {
//최대 유량 알고리즘 용 부모와 최단 거리
int parent[MAX_V], dist[MAX_V];
Edge *path[MAX_V];
queue<int> q;
//배열 초기화
memset(parent, -1, sizeof parent);
fill(dist, dist+MAX_V, INF);
//dfs
dist[S] = 0;
q.push(S);
while(!q.empty()) {
int here = q.front(); q.pop();
for(auto e : adj[here]) {
//탐색할 곳
int there = e->next;
//더 최단 경로가 존재할 경우 갱신
if(e->Remain() > 0 && dist[there] > dist[here] + e->cost) {
dist[there] = dist[here] + e->cost;
parent[there] = here;
path[there] = e;
q.push(there);
}
}
}
//더 이상의 증가 경로가 없으므로 종료
if(parent[E] == -1)
break;
//탐색한 경로로 흘려줄 수 있는 최대 유량
int flow = INF;
for(int p = E; p!=S; p = parent[p])
flow = min(flow, path[p]->Remain());
for(int p = E; p!=S; p = parent[p]) {
minCost += flow * path[p]->cost;
path[p]->Push(flow);
}
}
return minCost;
}
int main() {
ios_base::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
while(1) {
input();
cout << MCMF() << '\n';
}
return 0;
}
※현재 고등학교 등교 중인 학생입니다. 이제 알고리즘을 본격적으로 공부하기 시작해서 아직 초보입니다. 혹시 제가 잘못 알고 있는 점이나 더 좋은 풀이 방법이 있어 댓글에 남겨주시면 감사히 하나하나 열심히 읽어보겠습니다. 좋아요, 단순한 댓글 한마디라도 저에겐 큰 힘이 됩니다! 감사합니다.
'알고리즘 > 백준' 카테고리의 다른 글
[C++] 10254번 고속도로 - 기하학, 볼록 껍질, 회전하는 캘리퍼스 (0) | 2022.11.02 |
---|---|
[C++] 3683번 고양이와 개 - 이분 매칭 (0) | 2022.11.02 |
[C++] 1006번 습격자 초라기 - DP (0) | 2022.10.31 |
[C++] 19124번 Binomial Coefficient - 수학, 정수론 (0) | 2022.10.28 |
[C++] 2188번 축사 배정 - 이분 매칭 (0) | 2022.10.28 |
Comments