SPFA(Shortest Path First Algorithm) 알고리즘은 "음수 간선을 포함한 데이크스트라 알고리즘"으로 이해하면 편한 "벨만 포드 알고리즘"을 개선한 알고리즘 입니다.
BFS - 데이크스트라 - 벨만포드(SPFA)로 학습을 진행하시면 편합니다.
벨만 포드는 "모든 간선에 대해 업데이트를 진행"하지만, SPFA는 "바뀐 정점과 연결된 간선에 대해서만 업데이트를 진행"합니다.
벨만포드를 학습하지 않고, SPFA를 학습하셔도 태그로 벨만포드가 들어간 문제를 대부분 해결하실 수 있습니다.
ex)
https://www.acmicpc.net/problem/11657
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 2 4
1 3 3
2 3 -1
3 1 -2
1에서 2로 가는데 걸리는 최소 시간은 1 -> 2으로, 4이 소요됩니다.
1에서 3으로 가는데 걸리는 최소 시간은 1 -> 2 -> 3으로, 4 + -1 = 3이 소요됩니다.
음수 간선일 경우 cycle이 발생하므로, 해당 사이클이 발생하는지 체크 후, 발생하게 된다면 탈출하는 방식으로 해결하면 됩니다.
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.Queue;
import java.util.StringTokenizer;
public class Main {
static int n, m;
static ArrayList<int[]>[] arr;
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
n = Integer.parseInt(st.nextToken());
m = Integer.parseInt(st.nextToken());
arr = new ArrayList[n + 1];
for(int i = 0; i <= n; i++) {
arr[i] = new ArrayList<>();
}
while(m --> 0) {
st = new StringTokenizer(br.readLine());
int a = Integer.parseInt(st.nextToken()), b = Integer.parseInt(st.nextToken());
int val = Integer.parseInt(st.nextToken());
arr[a].add(new int[] {b, val});
}
System.out.print(spfa());
}
public static String spfa() {
Queue<Integer> queue = new LinkedList<>();
boolean[] visited = new boolean[n + 1];
long[] dist = new long[n + 1]; //가는데 걸리는 시간
int[] cycle = new int[n + 1]; // 사이클이 발생하는지 체크
Arrays.fill(dist, Integer.MAX_VALUE);//방문하지 않은 노드들에 가는 시간 max로 설정
queue.add(1);
dist[1] = 0;
cycle[1]++;
visited[1] = true;
while(!queue.isEmpty()) {
int now = queue.poll();
visited[now] = false;//다시 방문할 수 있으니 false로 설정
for(int i = 0; i < arr[now].size(); i++) {
int next = arr[now].get(i)[0];
int cost = arr[now].get(i)[1];
if(dist[next] > dist[now] + cost) {
dist[next] = dist[now] + cost;
if(!visited[next]) {
cycle[next]++;
if(cycle[next] >= n) {
return "-1";
}//사이클이 발생한다면 return
queue.add(next);
visited[next] = true;
}
}
}
}
StringBuilder sb = new StringBuilder();
for(int i = 2; i <= n; i++) {
sb.append(dist[i] != Integer.MAX_VALUE ? dist[i] : -1).append("\n");
}//갈 수 없는 노드는 -1, 갈수 있는 노드는 dist[i]
return sb.toString();
}
}
추천 문제
https://www.acmicpc.net/problem/1865
1865번: 웜홀
첫 번째 줄에는 테스트케이스의 개수 TC(1 ≤ TC ≤ 5)가 주어진다. 그리고 두 번째 줄부터 TC개의 테스트케이스가 차례로 주어지는데 각 테스트케이스의 첫 번째 줄에는 지점의 수 N(1 ≤ N ≤ 500),
www.acmicpc.net
https://www.acmicpc.net/problem/1738
1738번: 골목길
첫째 줄에 골목길들이 교차하는 지점의 개수 n (2 ≤ n ≤ 100)과 골목길의 개수 m (1 ≤ m ≤ 20,000) 이 차례로 주어진다. 이어지는 m개의 행에 각각의 골목길을 나타내는 세 정수 u, v, w가 차례로
www.acmicpc.net
'CS > 알고리즘' 카테고리의 다른 글
배열 vs ArrayList vs Set 무엇이 더 빠를까? (0) | 2023.03.01 |
---|---|
강한 연결 요소 (Strongly Connected Component) (0) | 2023.01.26 |
백트래킹(Backtracking) (0) | 2023.01.25 |
Dijkstra - 데이크스트라(다익스트라) (1) | 2023.01.22 |
BFS - 너비 우선 탐색 / DFS - 깊이 우선 탐색 (0) | 2023.01.22 |
댓글