본문 바로가기

Algorithm/코드 풀이

1753번: 최단경로

문제 설명

https://www.acmicpc.net/problem/1753

 

1753번: 최단경로

첫째 줄에 정점의 개수 V와 간선의 개수 E가 주어진다. (1≤V≤20,000, 1≤E≤300,000) 모든 정점에는 1부터 V까지 번호가 매겨져 있다고 가정한다. 둘째 줄에는 시작 정점의 번호 K(1≤K≤V)가 주어진다.

www.acmicpc.net

[문제]

 방향 그래프가 주어지면 주어진 시작점에서 다른 모든 정점으로의 최단 경로를 구하는 프로그램을 작성하시오. 단, 모든 간선의 가중치는 10 이하의 자연수이다.

 

[입력]

 첫째 줄에 정점의 개수 V와 간선의 개수 E가 주어진다. (1≤V≤20,000, 1≤E≤300,000) 모든 정점에는 1부터 V까지 번호가 매겨져 있다고 가정한다. 둘째 줄에는 시작 정점의 번호 K(1≤K≤V)가 주어진다. 셋째 줄부터 E개의 줄에 걸쳐 각 간선을 나타내는 세 개의 정수 (u, v, w)가 순서대로 주어진다. 이는 u에서 v로 가는 가중치 w인 간선이 존재한다는 뜻이다. u와 v는 서로 다르며 w는 10 이하의 자연수이다. 서로 다른 두 정점 사이에 여러 개의 간선이 존재할 수도 있음에 유의한다.

 

[출력]

 첫째 줄부터 V개의 줄에 걸쳐, i번째 줄에 i번 정점으로의 최단 경로의 경로값을 출력한다. 시작점 자신은 0으로 출력하고, 경로가 존재하지 않는 경우에는 INF를 출력하면 된다.

 

[예제 입력 1]

5 6
1
5 1 1
1 2 2
1 3 3
2 3 4
2 4 5
3 4 6

[예제 출력 1]

0
2
3
7
INF

풀이

 문제에서 특정 시작 정점이 주어지고, 해당 정점으로부터 다른 모든 정점까지의 최단 경로(가중치 값)를 출력하면 되기 때문에 전체 정점에 대하여 최단 경로를 구하는 플로이드 알고리즘보다는 다익스트라 알고리즘이 더 적합하다 판단되어 이를 적용했다. 또한 다익스트라 알고리즘 자체를 앞서 풀었던 합승 택시 요금에서도 이를 사용해봤기에 무리 없이 적용할 수는 있었다.

유의할 점

 하지만 문제는 바로 성능 향상에 있었다. 우선적으로 앞서 노드들의 가중치를 연결리스트로 구현하면서 (연결된 정점, 가중치) 형태로 구현했다 보니 다익스트라 알고리즘에서 우선 순위 큐에 해당 값을 넣을 때도 (연결된 정점, 가중치)의 pair 형태로 값을 넣었다. 그러다 보니 비용을 우선적으로 정렬이 되어야 할게 아니라 의도치 않은 연결된 정점들의 번호로 정렬이 진행되다 보니 시간 초과가 발생하였다.

 그 이후에는 자잘한 성능 향상이었는데, 기존에는 배열의 초기화 등에 있어 크기와 같은 관련 인자를 입력 받고 초기화 하던 것을 미리 최대값을 가지고 초기화를 진행해두었고, 입출력과 관련해서 추가적인 변화를 주었다.

 

<초기 코드>

#include <iostream>
#include <vector>
#include <queue>

using namespace std;

#define INF 98765432

void dijkstra(vector<int> &nodeGraph, vector<vector<pair<int, int>>> graph,int V, int K) {
	priority_queue<pair<int, int>> pq;
	pq.push(make_pair(K, 0));

	while (!pq.empty()) {
		int here = pq.top().first;
		int cost = -pq.top().second;
		pq.pop();

		if (nodeGraph[here] < cost) //이미 최저값이 있다면 넘어간다.
			continue;
		//나온 정점과 연결되어있는 정점들의 거리 최신화
		for (int i = 0; i < graph[here].size(); i++) {
			int next = graph[here][i].first;
			int newDist = cost + graph[here][i].second;

			if (newDist < nodeGraph[next]) {
				nodeGraph[next] = newDist;
				pq.push(make_pair(next, -newDist));
			}
		}
	}
}

int main() {
	int V, E, K;//정점의 개수 V, 간선의 개수 E, 시작정점 K
	vector<vector<pair<int, int>>> graph;
	int u, v, w;

	cin >> V >> E >> K;

	vector<int> nodeGraph(V + 1, INF);
	nodeGraph[K] = 0;

	for (int i = 0; i <= V; i++) {
		vector<pair<int, int>> temp;
		graph.push_back(temp);
	}

	for (int i = 0; i < E; i++) {
		cin >> u >> v >> w;
		graph[u].push_back(make_pair(v, w));
	}

	dijkstra(nodeGraph, graph, V, K);

	for (int i = 1; i <= V; i++) {
		if (nodeGraph[i] == INF)
			cout << "INF";
		else
			cout << nodeGraph[i];
		cout << endl;
	}

	return 0;
}

 

<최종 코드>

#include <iostream>
#include <vector>
#include <queue>

using namespace std;

#define INF 98765432

vector<pair<int, int>> graph[20001];//정점들의 링크 가중치 연결리스트
vector<int> nodeGraph(20001, INF);//시작 정점의 탐색 cost 벡터

void dijkstra(int K) {//다익스트라 알고리즘
	priority_queue<pair<int, int>> pq;
	pq.push(make_pair(0, K));

	while (!pq.empty()) {
		int here = pq.top().second;
		int cost = -pq.top().first;
		pq.pop();

		if (nodeGraph[here] < cost) //이미 최저값이 있다면 넘어간다.
			continue;
		//나온 정점과 연결되어있는 정점들의 거리 최신화
		for (int i = 0; i < graph[here].size(); i++) {
			int next = graph[here][i].first;
			int newDist = cost + graph[here][i].second;

			if (newDist < nodeGraph[next]) {
				nodeGraph[next] = newDist;
				pq.push(make_pair(-newDist, next));
			}
		}
	}
}

int main() {

	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);

	int V, E, K;//정점의 개수 V, 간선의 개수 E, 시작정점 K
	int u, v, w;

	cin >> V >> E >> K;

	nodeGraph[K] = 0; //입력받은 시작정점의 cost 값 0 초기화

	for (int i = 0; i < E; i++) {//연결리스트 형태로 저장
		cin >> u >> v >> w;
		graph[u].push_back(make_pair(v, w));
	}

	dijkstra(K);

	for (int i = 1; i <= V; i++) {//답안 출력
		if (nodeGraph[i] == INF)
			cout << "INF\n";
		else
			cout << nodeGraph[i] << "\n";
	}

	return 0;
}

결과

'Algorithm > 코드 풀이' 카테고리의 다른 글

1916번: 최소비용 구하기  (0) 2021.04.10
1937번: 욕심쟁이 판다  (0) 2021.04.02
2225번: 합분해  (0) 2021.03.21
1005번: ACM Craft  (0) 2021.03.21
1520번: 내리막 길  (0) 2021.03.13