https://www.acmicpc.net/problem/1504
1504번: 특정한 최단 경로
첫째 줄에 정점의 개수 N과 간선의 개수 E가 주어진다. (2 ≤ N ≤ 800, 0 ≤ E ≤ 200,000) 둘째 줄부터 E개의 줄에 걸쳐서 세 개의 정수 a, b, c가 주어지는데, a번 정점에서 b번 정점까지 양방향 길이 존
www.acmicpc.net
1번 노드에서 v1, v2 노드를 순서와 상관없이 거쳐서 N번 노드로 이동하는 문제이다. 1번 노드에서 N번 노드로 이동하는 경로가 항상 v1, v2 노드를 포함하는지 알 수 없으므로 1 -> v1 -> v2 -> N 경로와 1 -> v2 -> v1 -> N 두 경로를 비교해야 한다. 이 때 출발지가 시작점과 두 경유지 v0, v1까지 포함해 총 세 곳이므로, 다익스트라 기법 역시 세 번을 사용해야 한다. 1 -> v0 -> v1 -> N 경로에서는 1 -> v0, v0 -> v1, v1 -> N을 따로 계산해서 더해야 하고, 1 -> v1 -> v0 -> N 역시 1 -> v1, v1 -> v0, v0 -> N을 따로 계산해서 더해야 하기 때문.
#include <iostream>
#include <queue>
#include <vector>
#define INF 0x3f3f3f3f
#define MAX_N 800
#define _cost first
#define _node second
using namespace std;
typedef pair<int, int> edge;
typedef priority_queue<edge, vector<edge>, greater<edge>> pq;
// visit[i]: 노드 i에 방문했다면 true
bool visit[MAX_N + 1];
// N: 노드의 개수, v: 두 경유 노드
// cost: 다익스트라 알고리즘으로 구한 각 노드까지의 거리
int N, v[2], cost[MAX_N + 1] = { 0, };
// 모든 에지를 탐색하기 위한 우선순위 큐
pq q;
// graph[i]: 노드 i와 연결된 에지를 <cost, node> 꼴로 저장
vector<edge> graph[MAX_N + 1];
int min(int a, int b) { return a < b ? a : b; }
void push(edge e) {
// e를 방문했거나 사용해서 비용 감소가 발생하지 않는다면 return
if (visit[e._node] || cost[e._node] <= e._cost) return;
// e를 이용해서 그래프를 탐색한 뒤
visit[e._node] = true;
cost[e._node] = e._cost;
// e와 연결된 노드의 각 에지 중
for (auto e_ : graph[e._node])
// 아직 방문하지 못한 노드와 연결된 에지를 q에 push
if (!visit[e_._node])
q.push({cost[e._node] + e_._cost, e_._node});
}
// 다익스트라 알고리즘을 통해 start 노드에서 다른 노드까지의 비용을 계산
void dijkstra(int start) {
// visit과 cost를 초기화한 뒤
fill_n(visit, MAX_N + 1, false);
fill_n(cost, MAX_N + 1, INF);
q = pq();
// 시작 노드에서 시작 노드로 향하는 가상의 에지를 push
push({0, start});
// 목적지와 경유지를 모두 방문할 때까지
// 모든 에지를 비용이 작은 에지 순으로 탐색
while (!q.empty() &&
(cost[v[0]] == INF || cost[v[1]] == INF || cost[N] == INF)) {
push(q.top());
q.pop();
}
}
int main() {
// 입출력 속도 향상
ios::sync_with_stdio(false);
cin.tie(NULL);
cout.tie(NULL);
// E: 에지의 개수, a, b, c : 각 에지를 입력받을 때 사용할 공간
// v0v1: 1 -> v0 -> v1 -> N, v1v0: 1 -> v1 -> v0 -> N
// ans: v0v1과 v1v0 중 작은 값을 저장해 함수 호출을 줄인다
int E, a, b, c, v0v1, v1v0, ans = 0;
// 문제 조건 입력
cin >> N >> E;
while (E--) {
cin >> a >> b >> c;
graph[a].emplace_back(c, b);
graph[b].emplace_back(c, a);
}
cin >> v[0] >> v[1];
// 시작 노드와 v[0], v[1]에서 다른 노드까지 걸리는 비용을 각각 계산한 뒤
// 시작 노드에서 시작해 v[0], v[1] 노드를 모두 거쳐 노드 N까지 이동하는
// 경로의 최소 비용을 계산
dijkstra(1);
v0v1 = min(INF, cost[v[0]]);
v1v0 = min(INF, cost[v[1]]);
dijkstra(v[0]);
v0v1 = min(INF, v0v1 + cost[v[1]]);
v1v0 = min(INF, v1v0 + cost[N]);
dijkstra(v[1]);
v0v1 = min(INF, v0v1 + cost[N]);
v1v0 = min(INF, v1v0 + cost[v[0]]);
// 다익스트라 기법으로 얻은 경로의 최솟값을 계산
ans = v0v1 < v1v0 ? v0v1 : v1v0;
// 두 경유 노드를 거쳐 1 -> N 경로를 완성시킬 수 있다면 그 경로의 비용을 출력
cout << ((ans < INF) ? ans : -1) << '\n';
return 0;
}
'알고리즘 > 문제 풀이' 카테고리의 다른 글
1546번: 평균 (0) | 2021.12.08 |
---|---|
1541번: 잃어버린 괄호 (0) | 2021.12.08 |
1495번: 기타리스트 (0) | 2021.12.08 |
1476번: 날짜 계산 (0) | 2021.12.08 |
1475번: 방 번호 (0) | 2021.12.08 |