Skip to main content Link Menu Expand (external link) Document Search Copy Copied

01738번 골목길

Problem link: https://www.acmicpc.net/problem/1738

Keywords: Bellman Ford Algorithm

금품을 주울 때 해당 골목(간선)의 가중치를 음수로, 금품을 갈취 당할 때 해당 골목의 가중치를 양수로 표현한 뒤에 Bellman-Ford를 사용하면 쉽게 풀 수 있다. 다만, 여느 Bellma-Ford를 쓰는 문제들과 같이 음수 사이클이 생기는 경우를 잘 생각해서 처리해주어야한다. 이 문제에서 음수 사이클이 생긴다고 다짜고짜 -1을 출력하면 WA를 받게되므로 주의하자. 최적해가 존재하지 않아서 -1을 출력해야하는 상황은 아래와 같다.

  • 그래프 내에 음수 사이클이 존재하며,
  • 1번 정점에서 음수 사이클 내의 임의의 정점으로 갈 수 있고,
  • 음수 사이클 내의 임의의 정점에서 N번 정점으로 갈 수 있다.

첫번째 bullet은 간선의 relaxation 횟수를 살펴서 쉽게 처리할 수 있고, 두/세번째 bullet은 연결성 여부를 판단하기 위해 DFSBFS를 수행해주어야 한다. 내 경우에는 Bellman-Ford의 N번째 relaxation에서 relax된 간선의 정점들을 저장해두었기 때문에(이들이 곧 사이클 내부의 정점들이므로), 간편하게 multi-source, multi-destination search를 진핼할 수 있는 BFS를 사용하였다.

정말 별 것은 아니지만, 최적 경로를 backtrack하여 보여주는 과정은 간선 (u, v)가 relaxation될 때 prev[v]u를 저장해두는 것을 통해 처리할 수 있다. prev[x]에 마지막에 저장된 값이 결국 최적 경로에서 x의 predecessor가 되는 아이디어를 이용한 것이다.

#include <iostream>
#include <queue>
#include <set>
#include <stack>
#include <utility>
#include <vector>

using namespace std;

// Inputs.
constexpr int kMaxDist = 1'000'000;
int N;
int M;
vector<vector<pair<int, int>>> ADJ;

// Solution.
vector<int> DIST;
vector<int> PREV;
set<int> VERTICES_IN_CYCLE;
vector<bool> VISITED;

bool Bfs(const set<int>& sources, const set<int>& destinatios) {
  VISITED.assign(N, false);
  queue<int> q;
  for (int source : sources) {
    VISITED[source] = true;
    if (destinatios.find(source) != destinatios.end()) {
      return true;
    }
    q.emplace(source);
    while (!q.empty()) {
      const auto& here = q.front();
      q.pop();
      for (const auto& [there, _] : ADJ[here]) {
        if (!VISITED[there]) {
          VISITED[there] = true;
          if (destinatios.find(there) != destinatios.end()) {
            return true;
          }
          q.emplace(there);
        }
      }
    }
  }
  return false;
}

void Solve() {
  DIST[0] = 0;
  PREV[0] = -1;
  int it;
  for (it = 0; it < N; ++it) {
    bool has_relaxed = false;
    for (int u = 0; u < N; ++u) {
      for (const auto& [v, w] : ADJ[u]) {
        if (DIST[u] - w < DIST[v]) {
          has_relaxed = true;
          DIST[v] = DIST[u] - w;
          PREV[v] = u;
          if (it == N - 1) {
            VERTICES_IN_CYCLE.insert(u);
          }
        }
      }
    }
    if (!has_relaxed) {
      break;
    }
  }
  if (!VERTICES_IN_CYCLE.empty() && Bfs({0}, VERTICES_IN_CYCLE) &&
      Bfs(VERTICES_IN_CYCLE, {N - 1})) {
    cout << -1 << endl;
  } else {
    stack<int> path;
    int v = N - 1;
    while (PREV[v] != -1) {
      path.push(v);
      v = PREV[v];
    }
    path.push(v);
    while (!path.empty()) {
      cout << path.top() + 1 << ' ';
      path.pop();
    }
  }
}

int main() {
  // For faster IO.
  ios_base::sync_with_stdio(false);
  cout.tie(nullptr);
  cin.tie(nullptr);

  // Read inputs.
  cin >> N >> M;
  ADJ.assign(N, {});
  DIST.assign(N, kMaxDist);
  PREV.assign(N, -1);
  for (int it = 0; it < M; ++it) {
    int u, v, w;
    cin >> u >> v >> w;
    ADJ[u - 1].push_back({v - 1, w});
  }

  // Solve.
  Solve();

  return 0;
}