코드포스 938D Buy a Ticket
문제
도시가 N개 있고, 양방향 기찻길이 M개 있다(i번째 길은 u↔v를 비용 w로 잇는다). 각 도시 i에는 콘서트 티켓 값 a_i가 있다. 각 도시 i마다, 어떤 도시 j로 가서(머물러도 됨) 콘서트를 보고 다시 i로 돌아오는 데 드는 최소 비용을 구하면 된다.
- 입력: 1번째 줄에
N M. 다음M줄에u v w(기찻길). 그다음 줄에 콘서트 비용a_1 … a_N. - 출력: 도시
1부터N까지 각각의 최소 비용을 공백으로 구분해 출력. - 제한:
N, M ≤ 200,000, 비용은 크므로long long(최대 약 10^12).
접근
도시 j에서 콘서트를 보고 i로 돌아온다 = i → j 왕복 이동 비용 + j의 콘서트 비용. 모든 도시 각각에 대해 이걸 최소화해야 하니, 여러 시작점에서의 최단경로 같은 문제다.
여러 시작점이 필요할 땐 가상의 슈퍼 시작 정점(0번) 을 만든다. 0번을 각 도시 i와 잇되, 그 간선 비용을 콘서트 비용 a_i 로 둔다. 그리고 기찻길은 왕복이라 비용을 2w 로 둔다. 그러면 0번에서 다익스트라를 한 번 돌리는 것만으로, 각 도시까지의 최단 거리 = “어딘가서 콘서트 보고 왕복하는 최소 비용”이 된다.
핵심은 두 가지를 그래프 가중치로 녹인 것 — 콘서트 비용은 슈퍼소스→도시 간선에, 왕복 이동은 2w에.
풀이
전체 코드: thxwelchs/algorithm
#include <bits/stdc++.h>
using namespace std;
// 여러 시작 정점에 대해 최단경로가 필요 할 땐? 임의의 시작정점을 만들고, 각 원래 시작정점들과 연결시켜준 채로, 임의의 시작정점으로부터의 다익스트라 진행
// 여기서는 콘서트 비용이 각 정점에 대해 비용이 발생하므로, 임의의 시작정점에서 원래의 시작정점으로의 간선에 콘서트 비용을 추가해준다.
typedef long long ll;
const int MA = 2e5 + 1;
const ll INF = 2e17 + 100;
typedef pair<int, ll> pill;
typedef pair<ll, int> plli;
int N, M;
ll W[MA];
ll dist[MA];
vector<pill> g[MA];
void dij() {
priority_queue<plli, vector<plli>, greater<plli>> pq;
// 임의의 시작정점 0, (초기비용, 0 에서 0으로 가는 비용은 0)
pq.emplace(0, 0);
while(!pq.empty()) {
plli p = pq.top(); pq.pop();
ll hdist = p.first;
int here = p.second;
if(hdist > dist[here]) {
continue;
}
for(int i = 0; i < g[here].size(); i++) {
pill tp = g[here][i];
int there = tp.first;
ll weight = tp.second;
if(dist[there] > dist[here] + weight) {
dist[there] = dist[here] + weight;
pq.emplace(dist[there], there);
}
}
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(NULL); cout.tie(NULL);
cin >> N >> M;
for(int i = 0; i < M; i++) {
int u, v;
ll w;
cin >> u >> v >> w;
g[u].emplace_back(v, w * 2);
g[v].emplace_back(u, w * 2); // u -> v에 애초에 왕복 이동 비용을 설정해주었는데 역방향도 해주어야 하는가?
}
for(int i = 1; i <= N; i++) {
cin >> W[i];
g[0].emplace_back(i, W[i]);
g[i].emplace_back(0, W[i]);
}
for(int i = 1; i <= N; i++) {
dist[i] = INF;
}
dij();
for(int i = 1; i <= N; i++) {
cout << dist[i] << ' ';
}
return 0;
}
댓글
GitHub(giscus) 댓글은 설정 완료 후 활성화됩니다.