개인사
[G3] 백준 23801번 두 단계 최단 경로 2 C++ 다익스트라, 다이나믹 프로그래밍, unordered_set 본문
728x90

리뷰

https://www.acmicpc.net/problem/23801
X부터 Z까지 이동하며 P개의 서로 다른 중간 정점을 방문했는지 여부의 상태를 체크하며 최단 거리를 구하는 문제
전역 변수
- N : 배열의 최대 크기를 정의할 상수 변수
- n : 노드의 개수를 저장할 변수
- m : 간선의 개수를 저장할 변수
- p : 중간에 들러야할 서로 다른 중간 노드의 개수를 저장할 변수
- x, z : 시작 노드와 도착 노드의 번호를 저장할 변수
- Edge : 간선 정보를 정의할 구조체
- edges : 인접 리스트를 저장할 Edge타입 벡터 배열
- dic : 중간에 들러야할 노드의 번호를 저장할 해시셋
- Pos : 현재 노드의 번호와 누적 시간, 유효한 정점을 지나왔는지를 정의할 구조체, 누적 시간을 기준으로 오름차순 정렬한다.
함수
1. dijkstra
ll dijkstra() {
priority_queue<Pos> pq;
pq.push({ x, 0, false });
vector<vector<ll>> dist(n + 1, vector<ll>(2, 1e18));
dist[x][0] = 0;
while (!pq.empty()) {
auto [cn, ct, cf] = pq.top(); pq.pop();
if (dist[cn][cf] < ct) continue;
if (cn == z && cf == true) return dist[cn][cf];
for (const Edge& edge : edges[cn]) {
auto [nn, w] = edge;
ll nt = ct + w;
bool nf = dic.count(nn) ? true : cf;
if (dist[nn][nf] > nt) {
dist[nn][nf] = nt;
pq.push({ nn, nt, nf });
}
}
}
return -1;
}
X부터 Z까지 유효한 정점을 거쳐 도달하는 최단 거리를 구하는 함수
문제풀이
- n, m값을 입력 받고, m개의 간선 정보를 입력 받아 edges를 초기화한다.
- x, z, p값을 입력 받고, p개의 유효 정점의 번호를 입력 받아 dic을 초기화한다.
- dijkstra함수를 호출한다.
- Pos타입의 우선순위 큐 pq를 초기화하고, 초기값 {x, 0, false}를 push한다.
- n+1*2크기의 정수형 벡터 dist의 초기값을 매우 큰 값으로 초기화한다.
- dist[x][0]을 0으로 저장하여 시작점을 초기화한다.
- pq가 빌때까지 while루프를 수행하고, 매 루프마다 pq에서 요소를 빼내어 변수 cn, ct, cf에 파싱한다.
- 첫 번째 기저 조건으로 dist[cn][cf]가 ct보다 작은 경우 더 좋은 케이스가 있는 경우이므로 continue처리한다.
- 두 번째 기저 조건으로 cn이 z이고, cf가 true인 경우 dist[cn][cf]를 리턴한다.
- edges[cn]을 순회하며, 변수 nt에 이동 후 시간을 저장한다.
- 변수 nf는 현재 노드가 dic에 포함되어 있다면 true, 아니라면 cf로 초기화한다.
- dist[nn][nf]가 nt보다 클 경우 dist[nn][nf]를 nt로 저장하고, pq에 {nn, nt, nf}를 push한다.
- while루프가 종료될때까지 기저 조건에 도달하지 못한 경우 -1을 리턴한다.
- dijkstra함수의 리턴값을 출력한다.
트러블 슈팅
- Pos, dist, dijkstra의 반환값을 int타입으로 설정하여 WA를 받았다.
- 거리 관련 정수형을 long long타입으로 변경하여 AC를 받았다.
참고 사항
- 노드의 개수는 최대 10만개, 간선의 가중치는 최대 100만이므로 int범위를 넘어설 수 있다.
- dist는 유효 정점을 거쳤는지 여부를 체크하기 위해 상태를 관리해주었다.
- 중간 정점 Y는 (1 ≤ Y ≤ N, X ≠ Y ≠ Z)조건을 가진다.
정답 코드
#include<iostream>
#include<queue>
#include<vector>
#include<unordered_set>
using namespace std;
using ll = long long;
const int N = 1e5 + 1;
int n, m, p, x, z;
struct Edge {
int nn, w;
};
vector<Edge> edges[N];
unordered_set<int> dic;
struct Pos {
int cn;
ll ct;
bool flag;
bool operator<(const Pos& other) const {
return ct > other.ct;
}
};
ll dijkstra() {
priority_queue<Pos> pq;
pq.push({ x, 0, false });
vector<vector<ll>> dist(n + 1, vector<ll>(2, 1e18));
dist[x][0] = 0;
while (!pq.empty()) {
auto [cn, ct, cf] = pq.top(); pq.pop();
if (dist[cn][cf] < ct) continue;
if (cn == z && cf == true) return dist[cn][cf];
for (const Edge& edge : edges[cn]) {
auto [nn, w] = edge;
ll nt = ct + w;
bool nf = dic.count(nn) ? true : cf;
if (dist[nn][nf] > nt) {
dist[nn][nf] = nt;
pq.push({ nn, nt, nf });
}
}
}
return -1;
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
cin >> n >> m;
while (m--) {
int f, t, w; cin >> f >> t >> w;
edges[f].push_back({ t, w });
edges[t].push_back({ f, w });
}
cin >> x >> z >> p;
while (p--) {
int y; cin >> y;
dic.insert(y);
}
cout << dijkstra();
}728x90
'알고리즘 공부 > C++' 카테고리의 다른 글
| [G3] 백준 23286번 허들 넘기 C++ 플로이드 와샬 (0) | 2026.02.13 |
|---|---|
| [P4] 백준 16221번 모독 C++ 세그먼트 트리 (0) | 2026.02.11 |
| [G4] 백준 12786번 INHA SUIT C++ 다익스트라 (0) | 2026.02.08 |
| [G3] 백준 22868번 산책 (small) C++ 너비 우선 탐색, 정렬 (0) | 2026.02.07 |
| [G3] 백준 8972번 미친 아두이노 C++ 구현, 시뮬레이션 (0) | 2026.02.05 |
