1738번: 골목길
첫째 줄에 골목길들이 교차하는 지점의 개수 n (2 ≤ n ≤ 100)과 골목길의 개수 m (1 ≤ m ≤ 20,000) 이 차례로 주어진다. 이어지는 m개의 행에 각각의 골목길을 나타내는 세 정수 u, v, w가 차례로
www.acmicpc.net
이번에 풀어 볼 문제는 벨만포드 문제입니다.
문제 속에 핵심찾기.

문제를 읽어보면 어떤 경로를 갔을 때 금품을 갈취당하거나, 획득하게 됩니다.
따라서 비용이 음수인 간선과 양수인 간선이 존재하며, 금품의 양은 음수가 될 수 있다는것으로 보아 벨만포드 알고리즘을 적용하기에 적합하다는 것을 알 수 있습니다.
정답은 민승이네 집(1번 노드)에서 코레스코 콘도(N번 노드)로 금품의 양이 최대가 되는 경로를 출력하면 됩니다.
∴ 주의 할 점.
경우에 따라서는 최적의 경로라는 것이 존재하지 않는 상황이 발생한다. 어떠한 경우에 그런 상황이 발생하는지 생각해 보자. 그러한 경우에는 -1을 출력하도록 한다.
코레스코 콘도(N번 노드)까지 가는 길에 금품의 양이 무한대가 되는 싸이클이 존재한다면 -1을 출력해주어야 합니다.
풀이.
벨만포드 알고리즘은 특정 노드로부터 다른 노드까지 최적의 비용을 구할 수 있으며, 음의 간선이 포함된 경우에도 사용할 수 있습니다. 또한 음수 간선의 순환을 감지할 수 있습니다.
하지만 모든 영역에 대해 싸이클 여부를 체크하기 때문에 단순히 싸이클이 존재한다고 -1을 출력해서는 안됩니다.
민승이네 집(1번 노드)에서 코레스코 콘도(N번 노드)로 가는 경로에 싸이클이 존재할 경우에만 -1을 출력해 주어야하는게 핵심입니다.
비용에 -1을 곱한 값을 삽입하여 금품의 양이 최소가 되는 경로를 구해주겠습니다.
1. bfs로 N번 노드부터 1번 노드까지 역추적하여 방문 노드를 체크해주겠습니다.
2. 벨만포드 알고리즘을 통해 path배열을 만들어 도착노드에 출발노드를 기록해줍니다.
2 - 1. N번째 반복할 때 경로가 방문 노드일 경우 비용이 갱신된다면 음수의 싸이클이 존재하므로 -1을출력합니다.
3. 2 - 1번의 경우가 해당되지 않는다면 path배열에 기록했던 경로를 1번부터 순서대로 출력해줍니다.
구현.
const readline = require("readline");
const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout,
});
// 모든 길이 이어져있지 않을 수 있다.
// N에서 출발해서 갈 수 있는 노드의 경우에 무한 싸이클이 존재할 때에만 -1출력
let N, M;
let edges = [];
let dist = [];
let path = [];
let result = []; 
let visit = []; // 출발노드에서 도착노드까지 방문한 노드를 기록하기 위한 배열
let graph = []; // 도착노드에서부터 역추적을 위한 graph배열
function bfs(start) {
  let queue = [start];
  visit[start] = true;
  let index = 0;
  while (queue.length > index) {
    const n = queue[index++];
    graph[n].forEach((nextNode) => {
      if (!visit[nextNode]) {
        visit[nextNode] = true;
        queue.push(nextNode);
      }
    });
  }
}
function bf() {
  for (let i = 1; i <= N; i++) {
    for (let j = 0; j < M; j++) {
      const [start, end, cost] = edges[j];
      if (dist[end] > dist[start] + cost) {
        dist[end] = dist[start] + cost;
        path[end] = start;
        // 방문 노드에 싸이클이 생긴다면 true를 리턴
        if (i === N && visit[end]) return true;
      }
    }
  }
  return false;
}
rl.on("line", function (line) {
  if (!N) {
    [N, M] = line.split(" ").map((n) => +n);
    dist = Array.from({ length: N + 1 }, () => Infinity);
    visit = Array.from({ length: N + 1 }, () => false);
    graph = Array.from({ length: N + 1 }, () => []);
  } else {
    const [u, v, w] = line.split(" ").map((n) => +n);
    edges.push([u, v, -w]); // 음의 싸이클 존재를 구하기 위해 비용을 -1 곱한 값으로 넣어줍니다.
    graph[v].push(u); // 역추적하기 위해 도착과 출발을 반대로 넣어줍니다.
    if (edges.length === M) {
      dist[1] = 0;
      bfs(N); // 도착노드로 부터 출발노드까지 기록을 위해 N(도착노드)으로 시작해줍니다.
      const isInf = bf();
      if (isInf) console.log(-1);
      else {
        // path배열을 통해 N부터 1까지 순서대로 삽입.
        let start = N;
        while (true) {
          result.push(start);
          if (start === 1) break;
          start = path[start];
        }
  		
        // 1번부터 N번까지 출력
        console.log(result.reverse().join(" "));
      }
      rl.close();
    }
  }
});'Nodejs로 알고리즘 박살내기' 카테고리의 다른 글
| 프로그래머스 LV.4 지형 이동 js (1) | 2022.03.11 | 
|---|---|
| 백준 4386 별자리 만들기 js (0) | 2022.03.09 | 
| 백준 2493 탑 js (0) | 2022.01.15 | 
| 백준 2473 세 용액 js (0) | 2022.01.13 | 
| 백준 3020 개똥벌레 (0) | 2022.01.12 |