[알고리즘] 벨만-포드 알고리즘
벨만-포드 알고리즘
- i 개 이하의 간선을 거쳤을 때 나오는 최단거리를 간선이 0개에서 V - 1 개까지 일때까지로 구한다.
- i 개 이하의 간선을 거쳤을 때 목적지 to 로 가는 최단거리는 아래 중 최소값을 나타낸다.
- i - 1 개 이하의 거쳤을 때 목적지 to 로 가는 최단거리
- from -> to 로 가는 간선이라면, [i - 1 개 이하의 거쳤을 때 목적지 from 로 가는 최단거리] + [from -> to 로 가는 간선 가중치]
- 만약에 간선이 V - 1 개 보다 많이 거쳤을 때 최소값이 나온다면, 그거는 동일 노드를 여러번 거치면 더 최소값이 나온다는 것이므로, 음의 사이클이 존재한다는 의미
dist[to] = min(dist[to], dist[from] + w);
기본 예제
- N개의 도시가 주어지고, 각 도시들을 연결하는 도로와 해당 도로를 통행하는 비용이 주어질 때 한 도시에서 다른 도시로 이동하는데 쓰이는 비용의 최소값을 구하는 프로그램을 작성하세요.
입력 설명
- 첫 번째 줄에는 도시의 수N(N<=100)과 도로수 M(M<=200)가 주어지고, M줄에 걸쳐 도로정보와 비용이 주어진다. 만약 1번 도시와 2번도시가 연결되고 그 비용이 13이면 “1 2 13”으로 주어진다. 그 다음 마지막 줄에 출발도시와 도착도시가 주어진다.
5 7
1 2 5
1 3 4
2 3 -3
2 5 13
3 4 5
4 2 3
4 5 7
1 5
출력 설명
- 출발도시에서 도착도시까지 가는데 걸리는 최소비용을 출력한다. 음의 사이클이 존재할 경우 -1를 출력한다.
14
#include <string>
#include <cstring>
#include <vector>
#include <iostream>
#include <algorithm>
#include <queue>
#include <limits.h>
using namespace std;
class Edge;
void solution(int V, int startPos, int destPos, vector<Edge> edges);
vector<int> BellmanFord(int V, int startPos, vector<Edge> edges);
class Edge
{
public:
Edge(int f, int t, int w)
{
from = f;
to = t;
weight = w;
}
public:
int from;
int to;
int weight;
};
int main()
{
int V,E, startPos, destPos;
cin >> V >> E;
vector<Edge> edges = vector<Edge>();
for (int i = 1; i <= E; i++)
{
int from, to, w;
cin >> from >> to >> w;
edges.push_back(Edge(from, to, w));
}
cin >> startPos >> destPos;
solution( V, startPos, destPos, edges);
}
void solution(int V, int startPos, int destPos, vector<Edge> edges)
{
vector<int> dist = BellmanFord(V, startPos, edges);
// 음의 사이클 존재 여부 확인
for (auto it = edges.begin(); it != edges.end(); it++)
{
int from = (*it).from;
int to = (*it).to;
int w = (*it).weight;
if (dist[to] > dist[from] + w)
{
cout << -1 << endl;
return;
}
}
cout << dist[destPos] << endl;
}
vector<int> BellmanFord(int V, int startPos, vector<Edge> edges)
{
vector<int> dist = vector<int>(V + 1, INT_MAX);
dist[startPos] = 0;
for (int edgeCount = 1; edgeCount < V; edgeCount++)
{
for (auto it = edges.begin(); it != edges.end(); it++)
{
int from = (*it).from;
int to = (*it).to;
int w = (*it).weight;
dist[to] = min(dist[to], dist[from] + w);
}
}
return dist;
}
유사문제 (타임 머신)
구현 방식
- 위의 코드는 음의 사이클이 하나라도 있으면
-1
을 출력하는 방식이었는데 - 이번에는 해당 시작 노드에서부터의 경로 중에 음의 사이클이 없어야한다.
- 예를 들어서 1 번 노드는 간선 이 없고, 2, 3 번 노드가 음의 사이클을 가지는 간선이 있는경우
- 이런 경우에는 한번더 탐색해서 감소하는 구간이 있는지 판별해서 음의 사이클을 체크하는 구간에서
- 1 ~ V -1 개까지 간선 개수를 탐색했을때, 접근되지 못했던 노드의 간선이면
continue
로 넘어가준다.
#include <string>
#include <cstring>
#include <vector>
#include <iostream>
#include <algorithm>
#include <queue>
#include <limits.h>
using namespace std;
class Edge;
void solution(int V, int startPos, vector<Edge> edges);
vector<long long> BellmanFord(int V, int startPos, vector<Edge> edges);
class Edge
{
public:
Edge(int f, int t, int w)
{
from = f;
to = t;
weight = w;
}
public:
int from;
int to;
int weight;
};
int main()
{
int V,E, startPos, destPos;
cin >> V >> E;
vector<Edge> edges = vector<Edge>();
for (int i = 1; i <= E; i++)
{
int from, to, w;
cin >> from >> to >> w;
edges.push_back(Edge(from, to, w));
}
solution( V, 1, edges);
}
void solution(int V, int startPos, vector<Edge> edges)
{
vector<long long> dist = BellmanFord(V, startPos, edges);
for (auto it = edges.begin(); it != edges.end(); it++)
{
int from = (*it).from;
int to = (*it).to;
long long w = static_cast<long long>((*it).weight);
if (dist[to] == LLONG_MAX || dist[from] == LLONG_MAX)
continue;
if (dist[to] > dist[from] + w)
{
cout << -1 << endl;
return;
}
}
for (int v = 1; v <= V; v++)
{
if (startPos == v)
continue;
if (dist[v] == LLONG_MAX)
cout << -1 << endl;
else
cout << dist[v] << endl;
}
}
vector<long long> BellmanFord(int V, int startPos, vector<Edge> edges)
{
vector<long long> dist = vector<long long>(V + 1, LLONG_MAX);
dist[startPos] = 0;
for (int edgeCount = 1; edgeCount < V; edgeCount++)
{
for (auto it = edges.begin(); it != edges.end(); it++)
{
int from = (*it).from;
int to = (*it).to;
int w = (*it).weight;
if (dist[from] == LLONG_MAX)
continue;
dist[to] = min(dist[to], dist[from] + w);
}
}
return dist;
}