Top
問1.
$n$ 個の頂点と $m$ 本のコストつき無向辺が与えられます。
$0$ 番の頂点から $n-1$ 番の頂点まで最小コストで行きたいとき、そのコストを求めてください。ルートが無ければそれを指摘してください。
・$2 \le n \le 5{,}000$
・$1 \le m \le n(n-1)/2$
・$1 \le$ コスト $\le 10^{4}$

ダイクストラ。
今までコード 1. のように書いてきたけど、コード 2. のようにループ前にコストを比較するようにしたり、コード 3. のように seen 配列を使うようにすると、ランダムなグラフで速くなった。
ダイクストラの実装はバリエーションが多く、速度差に影響するものもあるので、自分の実装を見直すのは良いかもしれない。

コード 1.
#include <iostream>
#include <algorithm>
#include <queue>
#include <tuple>
using namespace std;

struct edge { int to, cost; };

int main(void) {
  constexpr int inf = 987654321;
  int n, m; scanf("%d%d", &n, &m);
  vector<vector<edge>> G(n, vector<edge>());
  for(int i=0; i<m; ++i) {
    int a, b, c; scanf("%d%d%d", &a, &b, &c);
    G[a].push_back(edge{b, c});
    G[b].push_back(edge{a, c});
  }
  vector<int> cost(n, inf);
  cost[0] = 0;
  priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> pq;
  // コスト、点番号
  pq.emplace(0, 0);
  while(!pq.empty()) {
    int _, v; tie(_, v) = pq.top(); pq.pop();
    for(edge e : G[v]) {
      if(cost[e.to] > cost[v] + e.cost) {
        cost[e.to] = cost[v] + e.cost;
        pq.emplace(cost[e.to], e.to);
      }
    }
  }
  int res = cost[n-1];
  if(res == inf) { res = -1; }
  printf("%d\n", res);
  return 0;
}
      

コード 2.
#include <iostream>
#include <algorithm>
#include <queue>
#include <tuple>
using namespace std;

struct edge { int to, cost; };

int main(void) {
  constexpr int inf = 987654321;
  int n, m; scanf("%d%d", &n, &m);
  vector<vector<edge>> G(n, vector<edge>());
  for(int i=0; i<m; ++i) {
    int a, b, c; scanf("%d%d%d", &a, &b, &c);
    G[a].push_back(edge{b, c});
    G[b].push_back(edge{a, c});
  }
  vector<int> cost(n, inf);
  cost[0] = 0;
  priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> pq;
  // コスト、点番号
  pq.emplace(0, 0);
  while(!pq.empty()) {
    int c, v; tie(c, v) = pq.top(); pq.pop();
    if(cost[v] < c) { continue; }          // ここを足しました。
    for(edge e : G[v]) {
      if(cost[e.to] > cost[v] + e.cost) {
        cost[e.to] = cost[v] + e.cost;
        pq.emplace(cost[e.to], e.to);
      }
    }
  }
  int res = cost[n-1];
  if(res == inf) { res = -1; }
  printf("%d\n", res);
  return 0;
}
      

コード 3.
#include <iostream>
#include <algorithm>
#include <queue>
#include <tuple>
using namespace std;

struct edge { int to, cost; };

int main(void) {
  constexpr int inf = 987654321;
  int n, m; scanf("%d%d", &n, &m);
  vector<vector<edge>> G(n, vector<edge>());
  for(int i=0; i<m; ++i) {
    int a, b, c; scanf("%d%d%d", &a, &b, &c);
    G[a].push_back(edge{b, c});
    G[b].push_back(edge{a, c});
  }
  vector<int> cost(n, inf);
  cost[0] = 0;
  vector<bool> seen(n);                    // この
  priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> pq;
  // コスト、点番号
  pq.emplace(0, 0);
  while(!pq.empty()) {
    int _, v; tie(_, v) = pq.top(); pq.pop();
    if(seen[v]) { continue; }              // 3行を
    seen[v] = true;                        // 足しました。
    for(edge e : G[v]) {
      if(cost[e.to] > cost[v] + e.cost) {
        cost[e.to] = cost[v] + e.cost;
        pq.emplace(cost[e.to], e.to);
      }
    }
  }
  int res = cost[n-1];
  if(res == inf) { res = -1; }
  printf("%d\n", res);
  return 0;
}
      


問2.
上記の答案で priority_queue に greater を指定するのを忘れてしまった場合にどうなるか考えてください。

コード1. の場合:
キューの優先度がおかしいだけなので誤った答えを出すわけではないが、無駄な処理が増えるため遅くなる。
極端に遅くなる例として、以下のようなグラフが考えられる。



#!/usr/bin/python2
# -*- coding: utf-8 -*-
# †
N = 5000
arr = []
for i in xrange(N-1):
    arr.append([i, i+1, 1])
val = 3
for i in reversed(xrange(0, N-2, 2)):
    arr.append([i, i+2, val])
    val += 1

M = len(arr)
print N, M
for a, b, c in arr:
    print a, b, c
      

コード2. の場合:
コード1 と同様に遅くなる。

コード3. の場合:
seen 配列の存在により、それぞれの頂点で最小(?)コストをどんどん確定していくため、速いが誤った答えを出す。
競プロ的にも、サンプルケースで誤った答えを出しやすくなるので、こっちの方がバグに気付きやすいかも?

(ΦωΦ)<つづく