Bellman - Ford 알고리즘은 다익스트라 알고리즘과 달리
모든 노드를 주기적으로 갱신시켜주기 때문에 \(O(V*E)\)가 걸리지만
음의 가중치를 포함한 노드들에 대해서도 성공적으로 탐색할 수 있다.
11657번: 타임머신
첫째 줄에 도시의 개수 N (1 ≤ N ≤ 500), 버스 노선의 개수 M (1 ≤ M ≤ 6,000)이 주어진다. 둘째 줄부터 M개의 줄에는 버스 노선의 정보 A, B, C (1 ≤ A, B ≤ N, -10,000 ≤ C ≤ 10,000)가 주어진다.
www.acmicpc.net
대표적인 벨만 - 포드를 사용한 문제이다.
우선 각 간선의 수만큼 입력을 받는데 편하게 계산하기 위해 좌표들에 -1씩 해서 받아줬다.
cin >> n >> m;
for (int i = 0; i < m; i++) {
cin >> a >> b >> c;
ar[a - 1].push_back({ b - 1,c });
}
문제 조건 중
만약 1번 도시에서 출발해 어떤 도시로 가는 과정에서 시간을 무한히 오래전으로 되돌릴 수 있다면 첫째 줄에 -1을 출력한다.
라는 조건이 있다.
이 문제를 풀 때에는 조심해야 할 점이 있는데 그래프에 음의 사이클이 포함되어 있는가?이다.
음의 사이클이 포함되어 있다면 그 사이클이 나오기 전까지의 노드들만 정상으로 갱신될 것이고 그 이후의
값들은 모두 음의 무한대 값이 나올 것이다.
void bellmanford() {
cost[0] = 0;
for (int i = 0; i < n - 1; i++) {
for (int j = 0; j < n; j++) {
for (auto& i : ar[j]) {
int nxt = i.first, d = i.second;
if (cost[j] != INF && cost[nxt] > cost[j] + d) {
cost[nxt] = cost[j] + d;
}
}
}
}
}
위와 같은 코드가 기본적인 벨만 - 포드의 틀이라 할 수 있는데 이 코드는 음의 가중치가 포함되어있는 그래프를
찾지 못한다.
- 각 vertax - 1 만큼 돌려주는 중첩 문에 대해서 (vertax - 1) + 1 만큼 돌려준다
- 현재 vertax -1까지 돌렸으면 모든 갱신이 완료된 것이다. 하지만,
- 맨 마지막(vertax ( -1 + 1))에 갱신이 된다면 사이클에 빠져 무한히 갱신하게 된다는 뜻이다
위와 같은 접근법으로 해결한다.
bool bellmanford(int start) {
dst[start] = 0;
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
for (auto& v : ar[j]) {
int nxt = v.first;
int d = v.second;
if (dst[j] != INF && dst[nxt] > dst[j] + d) {
dst[nxt] = dst[j] + d;
if (i == n - 1) return true;
}
}
}
}
return false;
}
n -1 이 아닌 n 번 돌리며 마지막에 갱신이 된다면 음의 가중치를 포함하고 있다는 뜻이다.
전체적인 소스 코드
#include <iostream>
#include <vector>
using namespace std;
using ll = long long;
const ll INF = 1e18;
int n, m, a, b, c;
vector<ll> dst(501, INF);
vector<pair<int, int>> ar[501];
bool bellmanford(int start) {
dst[start] = 0;
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
for (auto& v : ar[j]) {
int nxt = v.first;
int d = v.second;
if (dst[j] != INF && dst[nxt] > dst[j] + d) {
dst[nxt] = dst[j] + d;
if (i == n - 1) return true;
}
}
}
}
return false;
}
int main() {
ios::sync_with_stdio(false); cin.tie(0);
cin >> n >> m;
for (int i = 0; i < m; i++) {
cin >> a >> b >> c;
ar[a - 1].push_back({ b - 1,c });
}
if (bellmanford(0)) {
cout << -1 << endl;
}
else {
for (int i = 1; i < n; i++) {
int ans = dst[i] != INF ? dst[i] : -1;
cout << ans << '\n';
}
}
return 0;
}
'Algorithm' 카테고리의 다른 글
소수 구하기 (0) | 2020.12.09 |
---|---|
플로이드 - 와샬 알고리즘 (0) | 2020.10.19 |
유니온 파인드(Union-Find) (0) | 2020.10.15 |
순열과 조합 (0) | 2020.10.02 |
다익스트라 알고리즘 (0) | 2020.09.24 |