알고리즘과 언어/common

다익스트라 알고리즘

nextcoder 2022. 3. 2. 12:32

 

 

✔️ 다익스트라 최단 경로 알고리즘 개요 :

 

특정한 노드에서 출발하여 다른 모든 노드로 가는 최단 경로 계산

음의 간선이 없을 때 정상적으로 동작 (현실 세계와 유사)

그리디 알고리즘으로 분류 (매 상황에서 가장 비용이 적은 노드를 선택해 임의의 과정을 반복)

 

✔️ 동작 방법

 

1. 출발 노드 설정

2. 출발 노드 기준으로 각 노드의 최소 비용 저장

3. 방문하지 않은 노드 중에서 가장 비용이 적은 노드 선택 후 방문 노드로 설정

4. 해당 노드를 거쳐 특정한 노드로 가는 경우를 고려하여 최소 비용 갱신

5. 3~4번 반복

 

 

[문제]

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

 

 

방법 1. 2차원 배열을 사용하여 그래프 저장 (메모리 초과)

#include <iostream>

using namespace std;

#define INF 2e9

int map[20001][20001];
int dst[20001];
int visit[20001];
int V, E;


int find_smallest(){

	int MIN = INF;
	int index = -1;
	for(int i = 1; i<= V; i++)
	{
		if(visit[i])
			continue;
		if(dst[i] < MIN) {
			MIN = dst[i];
			index = i;
		}
	}
	return index;
}

void dijkstra(int startNum)
{
	int next;
	dst[startNum] = 0;
	visit[startNum] = 1;

	for(int i = 1; i <= V; i++)
	{
		if (map[startNum][i] != 0)
			dst[i] = map[startNum][i];
	}

	while(1)
	{
		next = find_smallest();
		if (next == -1)
			break;
		visit[next] = 1;

		for(int i = 1; i <= V; i++)
		{
			if (map[next][i] != 0 && map[next][i] + dst[next] < dst[i])
				dst[i] = map[next][i] + dst[next];
		}
	}
}

int main() {

	int startNum;
	int u, v, w;
	cin >> V >> E;

	cin >> startNum;

	for(int i = 1; i <= E; i++)
	{
		cin >> u >> v >> w;
		if (!map[u][v])
			map[u][v] = w;
		else
		{
			if (map[u][v] > w)
				map[u][v] = w;
		}
	}
	for(int i = 1; i <= V; i++)
		dst[i] = INF;

	dijkstra(startNum);

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

주어진 메모리 조건을 초과한다.

 

 

 

방법2. 그래프를 vector로 표현하는 방법 (시간 초과)

#include <iostream>
#include <vector>

#define INF 2e9

using namespace std;

vector<pair<int, int>>map[20001];
int dst[20001];
int visit[20001];
int V, E;

int find_smallest(){

	int MIN = INF;
	int index = -1;
	for(int i = 1; i<= V; i++)
	{
		if(visit[i])
			continue;
		if(dst[i] < MIN) {
			MIN = dst[i];
			index = i;
		}
	}
	return index;
}

void dijkstra(int startNum)
{
	int next;
	dst[startNum] = 0;
	visit[startNum] = 1;

	for(int i = 0; i < map[startNum].size(); i++)
	{
		dst[map[startNum][i].first] = map[startNum][i].second;
	} // 여기에서 그 loop을 쓸 수 있는지?

	while(1)
	{
		next = find_smallest();
		if (next == -1) {
			//cout << "out!" <<endl;
			break;
		}
		visit[next] = 1;

		for(int i = 0; i < map[next].size(); i++)
		{
			if (dst[map[next][i].first] > map[next][i].second + dst[next])
				dst[map[next][i].first] = map[next][i].second + dst[next];
		}
	}
}

int main() {

	ios_base::sync_with_stdio(0);
	cin.tie(0);
	
	int startNum;
	int u, v, w;
	cin >> V >> E;

	cin >> startNum;

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

	for(int i = 1; i <= V; i++)
		dst[i] = INF;

	dijkstra(startNum);

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

	return 0;
}

 

 

방법3. 우선순위 큐를 사용하는 방법 (OK)

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

#define INF 2e9

using namespace std;

vector<pair<int, int>>map[20001];
int dst[20001];
//int visit[20001];
int V, E;
priority_queue<pair<int, int>> PQ;

/*
int find_smallest(){

	int MIN = INF;
	int index = -1;
	for(int i = 1; i<= V; i++)
	{
		if(visit[i])
			continue;
		if(dst[i] < MIN) {
			MIN = dst[i];
			index = i;
		}
	}
	return index;
}
*/

void dijkstra(int startNum)
{
	int next;
	int distance;
	dst[startNum] = 0;

	PQ.push({0, startNum});

	while(!PQ.empty())
	{
		distance = - PQ.top().first;
		next = PQ.top().second;
		PQ.pop();

		for (int i = 0; i < map[next].size(); i++)
		{
			if (dst[map[next][i].first] > map[next][i].second + distance) {
				dst[map[next][i].first] = map[next][i].second + distance;
				PQ.push({-dst[map[next][i].first], map[next][i].first});
			}
		}
	}
}

int main() {

	ios_base::sync_with_stdio(0);
	cin.tie(0);


	int startNum;
	int u, v, w;
	cin >> V >> E;

	cin >> startNum;

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

	for(int i = 1; i <= V; i++)
		dst[i] = INF;

	dijkstra(startNum);

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

	return 0;
}

 

값을 음수화하여서 최소힙으로 만들었다.

백준에서

ios_base::sync_with_stdio(0);
cin.tie(0);

를 넣어줘야만 시간 초과가 안난다.

자세한 내용은

https://nextcoder.tistory.com/10

 

ios_base::sync_with_stdio(false); cin.tie(NULL);

주로 코딩 테스트를 C++로 풀었기 때문에 python과 같은 언어에 비해 시간 초과가 나지 않을 것이라 생각했다. 그렇게 백준에서 간단한 알고리즘을 돌리려 한 그 때.. time fail이 났고 위 구문을 추가

nextcoder.tistory.com

 

 

 

 

 

 

참고 사이트

 

https://m.blog.naver.com/ndb796/221234424646

 

23. 다익스트라(Dijkstra) 알고리즘

다익스트라(Dijkstra) 알고리즘은 다이나믹 프로그래밍을 활용한 대표적인 최단 경로(Shortest Path) 탐...

blog.naver.com