파티
문제 해석
- N명의 학생이 X번 마을에 갔다가 집으로 돌아오는데 최단시간을 오고 간다고 했을 때, 가장 많은 시간을 소비하는 학생이 누구인지 구하는 문제입니다.
- 각 마을 사이에는 총 M개의 단방향 도로들이 있고, 도로마다 소비되는 시간이 정해져 있습니다.
설계(10)
- 최단거리를 구하는 문제로 다익스트라 알고리즘을 사용해야 할 것으로 보입니다.
- 플로이드-와샬 알고리즘은 O(N^3)으로 시간초과가 날것으로 보이기 때문에 사용하지 않습니다.
- 우선, 각 마을에서 X번마을로 이동하는데 걸리는 최단시간과, X번 마을에서 다른 마을로 이동하는데 걸리는 최단시간 두개를 모든 마을에 대해서 탐색하는 경우를 생각할 수 있습니다.
- 이는 O(NxMx2log(N))=O(NMlogN)정도로, 1억에 가까운 시간복잡도가 되어서 가능은 할 것으로 보입니다.
- 문제의 의도는 위의 구현방법이 정해가 아닌 것 같아서 더 최적화가 필요했습니다.
- 주어지는 문제에서 모든 간선은 단방향으로, 정해진 방향의 반대방향을 가지는 그래프를 구하면, 모든 마을에서 X마을로 오는데 걸리는 최단시간을 구할 수 있습니다.
- 또한, 정해진 그래프를 이용하여 X에서 시작하여 다른 모든 마을로 가는데 걸리는 최단시간을 구하면 모든 경로의 최단시간을 구할 수 있습니다.
- 역방향 그래프를 이용한 최단거리 + 정방향 그래프를 이용한 최단거리의 최댓값을 구하면 원하는 값을 구할 수 있게 됩니다.
- 이 방법은 단 2번의 다익스트라 알고리즘 사용으로 구할 수 있기 때문에 O(MlogN)의 시간복잡도가 들게 됩니다.
구현(40)
코드1 (NMlogN)
#include <iostream>
#include <vector>
#include <cstring>
#include <queue>
#include <algorithm>
using namespace std;
#define MAXN 1001
vector<pair<int,int> > adj[MAXN];
int dist[MAXN];
struct node{
int cur,w;
bool operator < (const node& a)const{
return a.w < w;
}
};
priority_queue<node> pq;
int N,M,X;
int dijkstra(int start, int end){
memset(dist,-1,sizeof(dist));
while (!pq.empty()) pq.pop();
pq.push({start,0});
dist[start] = 0;
while (!pq.empty()){
int cur = pq.top().cur;
int cur_w = pq.top().w;
pq.pop();
if (dist[cur] != cur_w) continue;
if (cur == end) return cur_w;
for (int i = 0 ;i < adj[cur].size(); i++){
int next = adj[cur][i].first;
int next_w = adj[cur][i].second+cur_w;
if (dist[next]==-1 || dist[next]>next_w){
dist[next] = next_w;
pq.push({next,dist[next]});
}
}
}
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cin >> N >> M >> X;
memset(dist,-1,sizeof(dist));
while (M--){
int u,v,w;
cin >> u >> v >> w;
adj[u].push_back({v,w});
}
int ans = 0;
for (int i = 1; i<=N; i++){
ans = max(ans,dijkstra(i,X)+dijkstra(X,i));
}
cout << ans <<"\n";
return 0;
}
코드2 (MlogN)
#include <iostream>
#include <vector>
#include <cstring>
#include <queue>
#include <algorithm>
using namespace std;
#define MAXN 1001
vector<pair<int,int> > adj1[MAXN];
vector<pair<int,int> > adj2[MAXN];
int dist1[MAXN];
int dist2[MAXN];
priority_queue<pair<int,int> > pq;
int N,M,X;
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cin >> N >> M >> X;
memset(dist1,-1,sizeof(dist1));
memset(dist2,-1,sizeof(dist2));
while (M--){
int u,v,w;
cin >> u >> v >> w;
adj1[u].push_back({v,w});
adj2[v].push_back({u,w});
}
pq.push({0,X});
dist1[X] = 0;
while (!pq.empty()){
int cur = pq.top().second;
int cur_w = -pq.top().first;
pq.pop();
for (int i = 0 ;i < adj1[cur].size(); i++){
int next = adj1[cur][i].first;
int next_w = adj1[cur][i].second+cur_w;
if (dist1[next]==-1 || dist1[next]>next_w){
dist1[next] = next_w;
pq.push({-dist1[next],next});
}
}
}
pq.push({0,X});
dist2[X] = 0;
while (!pq.empty()){
int cur = pq.top().second;
int cur_w = -pq.top().first;
pq.pop();
for (int i = 0 ;i < adj2[cur].size(); i++){
int next = adj2[cur][i].first;
int next_w = adj2[cur][i].second+cur_w;
if (dist2[next]==-1 || dist2[next]>next_w){
dist2[next] = next_w;
pq.push({-dist2[next],next});
}
}
}
int ans = 0;
for (int i = 1; i <=N; i++){
ans = max(ans,dist1[i] + dist2[i]);
}
cout << ans <<"\n";
return 0;
}
디버깅
제출결과
- 코드1 : 116 ms, 코드2: 0 ms
마무리
- 다익스트라 알고리즘의 응용문제였습니다. 문제의 정해를 구하기 위해서는 약간의 아이디어가 필요합니다.