グラフ理論における第二最短経路

前提知識

グラフの表現方法、最短経路探索アルゴリズム、幅優先探索(BFS)の理解が必要です。

第二最短経路の分類

  • 一般第二経路(同一辺の重複利用可能)
  • 単純第二経路(同一辺の重複利用不可)
  • 厳密/非厳密第二経路(最短経路と等価/非等価)

一般第二経路

配列の最大値・次大値探索と類似した手法を用います。各頂点について最短距離と第二短距離を同時に管理します。

if (min_dist[target] > min_dist[current] + edge_weight) {
  second_dist[target] = min_dist[target];
  min_dist[target] = min_dist[current] + edge_weight;
  traversal_queue.push(target);
} else if (second_dist[target] > min_dist[current] + edge_weight) {
  second_dist[target] = min_dist[current] + edge_weight;
  traversal_queue.push(target);
}

単純第二経路

最短経路を構成する辺を順次除外し、除外後の最短経路を比較します。辺除外によって元の最短経路が使用不能となり、新たな経路が探索されます。

#include <iostream>
#include <vector>
#include <queue>
#include <cmath>
using namespace std;

struct Point { int x, y; };
struct Connection { int destination; double cost; };

const int MAX_NODES = 210;
double primary_dist[MAX_NODES], secondary_dist[MAX_NODES];
int predecessor[MAX_NODES];
vector<Connection> graph[MAX_NODES];

double compute_distance(Point p1, Point p2) {
  double dx = p1.x - p2.x;
  double dy = p1.y - p2.y;
  return sqrt(dx*dx + dy*dy);
}

void find_shortest_path(int start) {
  fill(primary_dist, primary_dist + MAX_NODES, 1e18);
  primary_dist[start] = 0.0;
  queue<int> q;
  q.push(start);
  
  while (!q.empty()) {
    int current = q.front(); q.pop();
    for (auto conn : graph[current]) {
      int next = conn.destination;
      double cost = conn.cost;
      if (primary_dist[next] > primary_dist[current] + cost) {
        primary_dist[next] = primary_dist[current] + cost;
        predecessor[next] = current;
        q.push(next);
      }
    }
  }
}

void find_shortest_path_without_edge(int start, int u, int v) {
  fill(secondary_dist, secondary_dist + MAX_NODES, 1e18);
  secondary_dist[start] = 0.0;
  queue<int> q;
  q.push(start);
  
  while (!q.empty()) {
    int current = q.front(); q.pop();
    for (auto conn : graph[current]) {
      int next = conn.destination;
      if (current == u && next == v) continue;
      double cost = conn.cost;
      if (secondary_dist[next] > secondary_dist[current] + cost) {
        secondary_dist[next] = secondary_dist[current] + cost;
        q.push(next);
      }
    }
  }
}

int main() {
  int nodes, edges;
  cin >> nodes >> edges;
  vector<Point> points(nodes + 1);
  
  for (int i = 1; i <= nodes; i++) {
    cin >> points[i].x >> points[i].y;
  }
  
  for (int i = 0; i < edges; i++) {
    int from, to;
    cin >> from >> to;
    double cost = compute_distance(points[from], points[to]);
    graph[from].push_back({to, cost});
    graph[to].push_back({from, cost});
  }
  
  find_shortest_path(1);
  double result = 1e18;
  int current = nodes;
  
  while (predecessor[current]) {
    find_shortest_path_without_edge(1, predecessor[current], current);
    result = min(result, secondary_dist[nodes]);
    current = predecessor[current];
  }
  
  printf("%.2f\n", result);
  return 0;
}

タグ: グラフ理論 最短経路 第二最短経路 アルゴリズム SPFA

5月15日 03:44 投稿