알고리즘/백준
SPFA(Shortest Path Faster Algorithm)
래울
2024. 6. 23. 14:10
SPFA, Shortest Path Faster Algorithm
SPFA는 최단 경로 알고리즘으로, MCMF(Mininum Cost Maximun Flow)를 구현할 때 사용된다.
BFS와 벨만포드 알고리즘을 섞은 것과 비슷하다. 모든 간선의 가중치가 같은 그래프에서 SPFA를 수행하면 BFS와 똑같이 동작한다.
최단 경로 알고리즘
다익스트라: O(ElogV)로 동작하지만, 음수 간선은 처리 불가
플로이드-와셜: 음수 간선은 처리 가능하지만, 모든 정점들간의 거리를 구하는데 사용되고 O(V^3)으로 동작
벨만-포드: 음수 간선 처리 가능, O(VE)
음수간선이 포함된 사이클이 존재한다면, 비용은 점점 작아져 -INF가 된다.
해당 정점이 N번 이상 방문되었는지를 체크하여, 음수사이클의 존재 여부를 확인할 수 있다.
음수간선 최소비용을 구할 때는 음수 비용의 경우 N * M * C로 언더플로우가 발생할 수 있기 때문에 정수 범위를 잘 고려해야한다.
이제 MCMF(Network Flow + SPFA)문제를 풀 준비가 됬다.!
문제
음수간선을 포함한 최단 경로: https://www.acmicpc.net/problem/11657
코드
#include <iostream>
#include <algorithm>
#include <vector>
#include <stack>
#include <queue>
#include <cstring>
#include <cmath>
#include <unordered_map>
using namespace std;
int N, M;
struct Node {
int n, cost;
};
vector<Node> graph[501];
queue<int> q;
int check[501];
long long answer[501];
int visitedCnt[501];
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> N >> M;
int a, b, c;
for (int i = 0; i < M; ++i)
{
cin >> a >> b >> c;
graph[a].push_back({ b, c });
}
for (int i = 1; i <= N; ++i) answer[i] = 1987654321;
q.push(1);
check[1] = 1;
answer[1] = 0;
int cnt = 0;
while (!q.empty())
{
int qSize = q.size();
for (int i = 0; i < qSize; ++i)
{
int now = q.front();
q.pop();
check[now] = 0;
visitedCnt[now] ++;
if (visitedCnt[now] >= N) {
// 해당 정점을 N 번 이상 방문하였다면.
cout << -1;
return 0;
}
for (int j = 0; j < graph[now].size(); ++j)
{
Node nxt = graph[now][j];
long long nextCost = answer[now] + nxt.cost;
if (nextCost >= answer[nxt.n]) continue;
answer[nxt.n] = nextCost;
if (check[nxt.n] == 0) {
check[nxt.n] = 1;
q.push(nxt.n);
}
}
}
}
for (int i = 2; i <= N; ++i)
cout << (answer[i] > 1987654000 ? -1 : answer[i]) << "\n";
return 0;
}