ツバサの備忘録

主に備忘録代わりに精進記録を載せていくつもりです。

Benelux Algorithm Programming Contest 2016 E Charles in Charge

問題

N個の頂点と、M個の重み付きの辺があるグラフが与えられます。
頂点1から頂点Nに行く最短経路に、X%加えた新しい距離以内で、頂点1から頂点Nにたどる道の中で辺の重みの最大値が、最も小さくなるときの、辺の重みの最大値を求めなさい、というものです。

解法

まずは素直にダイクストラ法を利用して最短経路を求めます。
その後、X%加えた新しい距離を出します。これをXとします。
今回求めるのは辺の重みの最大値を最小化したものになるので、二分探索を利用します。
現在見ている値をDとしたとき、

  • 辺の重みがD以下のもののみを使用して、X以内に頂点1から頂点Nにたどり着くことができるか

という判定方法を利用して二分探索をします。
最終的に残ったDが答えになります。
やっていることはすごくシンプルなダイクストラ法と、ときどき見かける二分探索の組み合わせになります。最近二分探索をちょくちょく書いていたので、バグ無しで通すことができました。うれしいです。

提出コードは以下になります。

#include <bits/stdc++.h>
#define inf 1000000000000000000
using namespace std;

long long memo[10005] = {0};
vector<pair<long long, long long>> edge[10005];
long long n, m, x, minn, maxd = 0;
long long c1, c2, t;
priority_queue<pair<long long, long long>,
               vector<pair<long long, long long>>,
               greater<pair<long long, long long>>>
    pq;

void dk(long long d);
long long solve();

int main() {
  int i;
  cin >> n >> m >> x;
  for(i = 0; i < m; ++i) {
    cin >> c1 >> c2 >> t;
    --c1;
    --c2;
    edge[c1].push_back(make_pair(t, c2));
    edge[c2].push_back(make_pair(t, c1));
    maxd = max(maxd, t);
  }
  dk(inf);
  minn = memo[n - 1] * (double)(1 + 0.01 * x);
  cout << solve() << endl;
  return 0;
}

void dk(long long d) {
  long long nd, np;
  pair<long long, long long> now, nextn;
  memo[0] = 0;
  for(int i = 1; i < n; ++i) memo[i] = inf;
  pq.push(make_pair(0, 0));
  while(pq.size() > 0) {
    now = pq.top();
    pq.pop();
    nd = now.first;
    np = now.second;
    for(int i = 0; i < edge[np].size(); ++i)
      if(edge[np][i].first <= d) {
        nextn = make_pair(edge[np][i].first + nd,
                          edge[np][i].second);
        if(nextn.first < memo[nextn.second]) {
          memo[nextn.second] = nextn.first;
          pq.push(nextn);
        }
      }
  }
}

long long solve() {
  long long l = 0, r = maxd, now;
  while(r - l > 0) {
    now = (l + r) / 2;
    if(r - l == 1) {
      dk(l);
      if(memo[n - 1] <= minn)
        now = l;
      else
        now = r;
      break;
    }
    dk(now);
    if(memo[n - 1] <= minn)
      r = now;
    else
      l = now;
  }
  return now;
}