인접한 모든 정점을 넣는 것이 아니라 타겟 정점으로부터 dist 테이블에 기록된 최소비용의 정점을 넣음.
bfs 는 모든 정점에 대한 가중치 없음
다익스트라의 경우, 가중치가 존재함.
-> 이 때의 시간 복잡도 최악 V 제곱임.
- 이유 : 최단 거리 탐색 : V / 해당 정점으로부터 인접한 정점 검사 : v
- 각 정점마다 시작점까지의 최단 경로를 가지고 있는 cost 값이 필요함.
#include <iostream>
#include <mutex> // mutex 를 사용하기 위해 필요
#include <thread>
using namespace std;
#pragma comment(lib, "StaticLib1")
#include <vector>
#include <filesystem>
#include <queue>
// 각 정점마다 연결되는 정점들이 있음.
struct V
{
int num;
int cost;
};
vector<vector<V>> v(7);
vector<int> best(7, 1e9);
vector<bool> visited(7, false);
int MostSmall()
{
int vertex = -1;
int cost = 1e9;
// 방문하지 않은 것들 중에서 가장 cost 비용이 작은 정점을 추출하자.
for (int i = 0; i < best.size(); ++i)
{
if (best[i] < cost && visited[i] == false)
{
cost = i;
}
}
return cost;
}
int main()
{
// 1번 노드가 시작점.
// 모든 정점의 최소 비용을 구하자.
{
v[1].push_back({ 2,2 });
v[1].push_back({ 4,1 });
v[1].push_back({ 3,5 });
v[2].push_back({ 4,2 });
v[2].push_back({ 3,3 });
v[3].push_back({ 2,3 });
v[3].push_back({ 6,5 });
v[4].push_back({ 3,3 });
v[4].push_back({ 5,1 });
v[5].push_back({ 3,1 });
v[5].push_back({ 6,2 });
}
// 1. 타겟 정점으로부터 가장 최소 비용을 잡은 후, -> 방문 체크하자.
// 2. 인접한 정점의 dp 테이블 갱신.
int startNum = 1;
best[startNum] = 0;
visited[startNum] = true;
int edge = 1e9;
// 인접한 것들 중에서
// 1번 정점부터 시작함.
// 각 cost 갱신하기
// 시작 할때는 비교 안하고 그냥 셋팅.
for (int i = 0; i < v[startNum].size(); ++i)
{
int vertex = v[startNum][i].num;
best[vertex] = v[startNum][i].cost;
}
// 여기서 반복함.
// 언제 끝낼지를 확인해야 하는데. 내가 아는 정보는 정점들 중에서 startNum 만 끝남.
for (int k = 0; k < v.size() - 1; ++k)
{
// 최소값 구하기
int targetV = MostSmall();
if (targetV == 1e9)
break;
visited[targetV] = true;
for (int i = 0; i < v[targetV].size(); ++i)
{
int vertex = v[targetV][i].num;
int cost = v[targetV][i].cost + best[targetV];
// 방문하지 않은 것들 중에서 비교하자.
if (best[vertex] > cost)
{
best[vertex] = cost;
}
}
}
cout << endl;
// 찾은 정점을 통해서 또 진행..
}
위 코드를 보면,
방문하지 않은 것중에서 최소비용을 얻는 작업을 하고 있음. (MostSmall) ->이 비용을 줄이기 위해
인접한 정점에 cost 작업을 할 때, 그와 동시에 priority_queue 에 삽입하자.
삽입하는 데이터는 최소비용인 cost 와 인접한 정점을 넣으면 될 듯함.
왜냐하면 MostSmall 함수에서 best 중에서 가장 작은 정점을 반환하고 있기 때문임.
#include <iostream>
#include <mutex> // mutex 를 사용하기 위해 필요
#include <thread>
using namespace std;
#pragma comment(lib, "StaticLib1")
#include <vector>
#include <filesystem>
#include <queue>
// 각 정점마다 연결되는 정점들이 있음.
struct V
{
public :
int num;
int cost;
};
struct cmp
{
bool operator()(const V& _a, const V& _b)
{
return _a.cost > _b.cost;
}
};
// 이상하게 class 외부에다가 해줘야 오류 해결됨..
//bool operator>(const V& _a, const V& _b)
//{
// return _a.cost > _b.cost;
//}
//
//bool operator<(const V& _a, const V& _b)
//{
// return _a.cost < _b.cost;
//}
vector<vector<V>> v(7);
vector<int> best(7, 1e9);
vector<bool> visited(7, false);
int MostSmall()
{
int vertex = -1;
int cost = 1e9;
// 방문하지 않은 것들 중에서 가장 cost 비용이 작은 정점을 추출하자.
for (int i = 0; i < best.size(); ++i)
{
if (best[i] < cost && visited[i] == false)
{
cost = i;
}
}
return cost;
}
int main()
{
// 1번 노드가 시작점.
// 모든 정점의 최소 비용을 구하자.
{
v[1].push_back({ 2,2 });
v[1].push_back({ 4,1 });
v[1].push_back({ 3,5 });
v[2].push_back({ 4,2 });
v[2].push_back({ 3,3 });
v[3].push_back({ 2,3 });
v[3].push_back({ 6,5 });
v[4].push_back({ 3,3 });
v[4].push_back({ 5,1 });
v[5].push_back({ 3,1 });
v[5].push_back({ 6,2 });
}
// 1. 타겟 정점으로부터 가장 최소 비용을 잡은 후, -> 방문 체크하자.
// 2. 인접한 정점의 dp 테이블 갱신.
int startNum = 1;
best[startNum] = 0;
int edge = 1e9;
// 인접한 것들 중에서
priority_queue<V , vector<V> , cmp> pq;
// 시작점을 넣자.
pq.push({ 1,0 });
while ( !pq.empty())
{
V targetV = pq.top();
pq.pop();
int targetNum = targetV.num;
int targetNumCost = targetV.cost;
//if (visited[targetNum] == false)
// continue;
//visited[targetNum] = true;
// 인접한 정점들을 pq에 넣고, // 그리고 dp 값 갱신.
for (int i = 0; i < v[targetNum].size(); ++i)
{
int vertex = v[targetNum][i].num;
int cmpData = v[targetNum][i].cost + best[targetNum];
if (best[vertex] > cmpData)
{
best[vertex] = cmpData;
pq.push(v[targetNum][i]);
}
}
}
for (int i = 0; i < best.size(); ++i)
{
cout << i << "번의 최소비용은 : " << best[i] << endl;
}
cout << endl;
// 찾은 정점을 통해서 또 진행..
}
-> 위의 코드는 pq에 대해서 공부가 덜 된 상태에서 작성한 코드다. 240518
#include <iostream>
#include <mutex> // mutex 를 사용하기 위해 필요
#include <thread>
using namespace std;
#include <vector>
#include <filesystem>
#include <queue>
// 각 정점마다 연결되는 정점들이 있음.
struct V
{
public:
int num;
int cost;
bool operator>(const V& _obj) const
{
return cost > _obj.cost;
}
};
struct cmp
{
bool operator()(const V& _a, const V& _b)
{
return _a.cost > _b.cost;
}
};
vector<vector<V>> v(7);
vector<int> best(7, 1e9);
vector<bool> visited(7, false);
int MostSmall()
{
int vertex = -1;
int cost = 1e9;
// 방문하지 않은 것들 중에서 가장 cost 비용이 작은 정점을 추출하자.
for (int i = 0; i < best.size(); ++i)
{
if (best[i] < cost && visited[i] == false)
{
cost = i;
}
}
return cost;
}
int main()
{
// 1번 노드가 시작점.
// 모든 정점의 최소 비용을 구하자.
{
v[1].push_back({ 2,2 });
v[1].push_back({ 4,1 });
v[1].push_back({ 3,5 });
v[2].push_back({ 4,2 });
v[2].push_back({ 3,3 });
v[3].push_back({ 2,3 });
v[3].push_back({ 6,5 });
v[4].push_back({ 3,3 });
v[4].push_back({ 5,1 });
v[5].push_back({ 3,1 });
v[5].push_back({ 6,2 });
}
// 1. 타겟 정점으로부터 가장 최소 비용을 잡은 후, -> 방문 체크하자.
// 2. 인접한 정점의 dp 테이블 갱신.
int startNum = 1;
best[startNum] = 0;
int edge = 1e9;
// 인접한 것들 중에서
priority_queue<V, vector<V>, greater<V>> pq;
// 시작점을 넣자.
pq.push({ 1,0 });
while (!pq.empty())
{
V targetV = pq.top();
pq.pop();
int targetNum = targetV.num;
int targetNumCost = targetV.cost;
//if (visited[targetNum] == false)
// continue;
//visited[targetNum] = true;
// 인접한 정점들을 pq에 넣고, // 그리고 dp 값 갱신.
for (int i = 0; i < v[targetNum].size(); ++i)
{
int vertex = v[targetNum][i].num;
int cmpData = v[targetNum][i].cost + best[targetNum];
if (best[vertex] > cmpData)
{
best[vertex] = cmpData;
pq.push(v[targetNum][i]);
}
}
}
for (int i = 0; i < best.size(); ++i)
{
cout << i << "번의 최소비용은 : " << best[i] << endl;
}
cout << endl;
// 찾은 정점을 통해서 또 진행..
}
https://github.com/ndb796/python-for-coding-test/blob/master/9/2.cpp
결과
: 매순간 최소의 비용을 나타내는 정점을 선택해서 distTable을 갱신해서
최단 거리를 구하는 알고리즘이다.
: 타겟 노드에서 node번호까지 이동하는데 최단경로를 의미함.
: 다음 cost 비용을 계산하는 것은 현재 자기 자신까지의 cost와
외부에서 입력된 cost를 더한 후 dist값과 비교하는 것이다.
이 부분이 제일 중요하다.
0) 각 정점에 대한 distTable을 만들자.
1) 시작 정점을 1이라고 가정하자. 1번 정점의 dist는 0이 되고, 나머지 정점들을 모두 무한대 값으로 설정한다.
1-1) 시작정점으로 부터 연결된 정점들까지의 거리를 저장공간에 집어넣는다.
2) 정점 중 체크 안된 정점 중에서 값이 가장 작은 놈을 체크 후,
해당 정점으로부터 이동 가능한 모든 정점의 값을 갱신하는데,
갱신할때는 타겟으로 잡은 곳의 값에 연결된 비용을 더한 값과
연결된 정점들의 현재 저장된 값을 비교한다.
현재의 값과, 갱신된 값을 비교해 갱신여부를 확인한다.
현재의 값과 갱신된 값중 가장 작은 값을 적용한다.
3) 2번 작업을 반복한다.
=> 이런식으로 반복하다보면, 모든 정점까지 체크 완료된 테이블이 만들어진다.
-> 의구심이 생길 수 있는 부분이, 체크된 정점을 다시 확인이 불가능한 시퀀스인데, 만약에 다른 정점을 통해 4번 노드의 값이 최단거리로 갱신될수 있지 않을까 생각을 할 수 있다.
아래의 증명 과정을 통해 다익스트라는 그리디 알고리즘이라는 것을 생각할 수 있다. 매순간 최소의 비용은 나타내는 정점을 선택해서 distTable을 갱신해서
최단 거리를 구하는 알고리즘이다.
구종만 p.921 참고.
하지만 이것을 증명할 수 있는 조건이 있다.
1번 정점이 완료 된 후에 2,3,4,5,6번 정점 중 가장 작은 거리에 있는 정점을 선택한다
이 용어가 의미하는 것은 그 이외의 값으로 4번 정점으로 들어올때는 더 낮은 비용을 만들 수 없다는 것을 의미한다. 왜냐하면 거리는 모두 양수이기 때문이다.
거리가 음수이면 이 증명은 잘못된 것이다.
확인을 해보자.
-> 4번이 선택된다. 2번이 4번으로 들어 올수 있지만, 2번의 현재값은 2이다.
갱신이 되더라고 4번의 값 1보다는 작을 수 없다.
-> 다른 정점이 연결되어있다고 가정한다면, 5번이 연결되어 있다고 가정해보자.
전제 조건으로 체크안된 정점 중 가장 작은 정점이기 때문에 (현재 4번은 1이다.)
5번은 다른 정점에 의해 1초과된 값으로 되어 있을 것이다! 라고 확증이 가능하므로, 4번 값은 더 낮은 값으로 갱신이 될수 없다.
: 코드를 어떻게 작성할 것인가?
: 페이지 230과 같은 그림이 주어지고, 페이지 249와 같은 입력이 주어졌을때 각 정점에 도착하는데 최단 거리는 어떻게 될것인가?
그림
예제는 책에 있는 거 참고 : 페이지 249
#include <iostream>
#include <vector>
#include <queue>
#define INF 1e9 // 무한을 의미하는 값으로 10억을 설정
using namespace std;
// 노드의 개수(N), 간선의 개수(M), 시작 노드 번호(Start)
// 노드의 개수는 최대 100,000개라고 가정
int n, m, start;
// 각 노드에 연결되어 있는 노드에 대한 정보를 담는 배열
vector<pair<int, int> > graph[100001];
// 최단 거리 테이블 만들기
int d[100001];
void dijkstra(int start) {
priority_queue<pair<int, int>, vector<pair<int,int>>,
less<pair<int,int>>> pq;
// 시작 노드로 가기 위한 최단 경로는 0으로 설정하여, 큐에 삽입
pq.push({ 0, start });
d[start] = 0;
while (!pq.empty()) { // 큐가 비어있지 않다면
// 가장 최단 거리가 짧은 노드에 대한 정보 꺼내기
int dist = pq.top().first; // 현재 노드까지의 비용
int now = pq.top().second; // 현재 노드
pq.pop();
// 현재 노드가 이미 처리된 적이 있는 노드라면 무시
//210806 이부분 이해하는 것이 중요함.
//정점의 테이블 값보다 값이 크다면 밑의 반복문을 할 필요가 없다.
if (d[now] < dist) continue;
// 현재 노드와 연결된 다른 인접한 노드들을 확인
for (int i = 0; i < graph[now].size(); i++) {
int cost = dist + graph[now][i].second;
// 현재 노드를 거쳐서, 다른 노드로 이동하는 거리가 더 짧은 경우
if (cost < d[graph[now][i].first]) {
d[graph[now][i].first] = cost;
pq.push(make_pair(cost, graph[now][i].first));
}
}
}
}
int main(void) {
cin >> n >> m;
// 모든 간선 정보를 입력받기
for (int i = 0; i < m; i++) {
int a, b, c;
cin >> a >> b >> c;
// a번 노드에서 b번 노드로 가는 비용이 c라는 의미
graph[a].push_back({ b, c });
}
// 최단 거리 테이블을 모두 무한으로 초기화
fill(d, d + 100001, INF);
// 다익스트라 알고리즘을 수행
dijkstra(1);
// 모든 노드로 가기 위한 최단 거리를 출력
for (int i = 1; i <= n; i++) {
// 도달할 수 없는 경우, 무한(INFINITY)이라고 출력
if (d[i] == INF) {
cout << "INFINITY" << '\n';
}
// 도달할 수 있는 경우 거리를 출력
else {
cout << d[i] << '\n';
}
}
}
#include <iostream>
#include <vector>
#include <queue>
#define INF 1e9 // 무한을 의미하는 값으로 10억을 설정
using namespace std;
// 노드의 개수(N), 간선의 개수(M), 시작 노드 번호(Start)
// 노드의 개수는 최대 100,000개라고 가정
int n, m, start;
// 각 노드에 연결되어 있는 노드에 대한 정보를 담는 배열
vector<pair<int, int> > graph[100001];
// 최단 거리 테이블 만들기
int d[100001];
//거리가 가장 작은 놈이 top으로 와야 한다..
struct comp
{
bool operator()(pair<int, int>a, pair<int, int>b)
{
return a.first > b.first;
}
};
void dijkstra(int start) {
priority_queue<pair<int, int>, vector<pair<int,int>>, comp > pq;
// 시작 노드로 가기 위한 최단 경로는 0으로 설정하여, 큐에 삽입
pq.push({ 0, start });
d[start] = 0;
while (!pq.empty()) { // 큐가 비어있지 않다면
// 가장 최단 거리가 짧은 노드에 대한 정보 꺼내기
int dist = pq.top().first; // 현재 노드까지의 비용
int now = pq.top().second; // 현재 노드
pq.pop();
// 현재 노드가 이미 처리된 적이 있는 노드라면 무시
//210806 이부분 이해하는 것이 중요함.
//정점의 테이블 값보다 값이 크다면 밑의 반복문을 할 필요가 없다.
if (d[now] < dist) continue;
// 현재 노드와 연결된 다른 인접한 노드들을 확인
for (int i = 0; i < graph[now].size(); i++) {
int cost = dist + graph[now][i].second;
// 현재 노드를 거쳐서, 다른 노드로 이동하는 거리가 더 짧은 경우
if (cost < d[graph[now][i].first]) {
d[graph[now][i].first] = cost;
pq.push(make_pair(cost, graph[now][i].first));
}
}
}
}
int main(void) {
cin >> n >> m;
// 모든 간선 정보를 입력받기
for (int i = 0; i < m; i++) {
int a, b, c;
cin >> a >> b >> c;
// a번 노드에서 b번 노드로 가는 비용이 c라는 의미
graph[a].push_back({ b, c });
}
// 최단 거리 테이블을 모두 무한으로 초기화
fill(d, d + 100001, INF);
// 다익스트라 알고리즘을 수행
dijkstra(1);
// 모든 노드로 가기 위한 최단 거리를 출력
for (int i = 1; i <= n; i++) {
// 도달할 수 없는 경우, 무한(INFINITY)이라고 출력
if (d[i] == INF) {
cout << "INFINITY" << '\n';
}
// 도달할 수 있는 경우 거리를 출력
else {
cout << d[i] << '\n';
}
}
}
6 11
1 2 2
1 3 5
1 4 1
2 3 3
2 4 2
3 2 3
3 6 5
4 3 3
4 5 1
5 3 1
5 6 2
0
2
3
1
2
4
6 9
1 2 12
1 3 4
2 1 2
2 3 5
2 5 5
3 4 5
4 2 2
4 5 5
6 4 5
2 : 11
3 : 4
4 : 9
5 : 14