Strorkisのブログ

信憑性については悪しからず

ABC079D

問題

D - Wall

 iから jに書き換えるには魔力 c_{i,j}(0 \leq i,j \leq 9)が必要であるとき、 A_{i,j}を全て1に変えるのに必要な魔力の最小量を求めよ。

ただし、 A_{i,j} = -1の場合は変えなくて良い。

解法

制約上、ワーシャルフロイド法で解けそうだったが、ダイクストラ法の経験が乏しかったため、ダイクストラ法で解くことにした。

考え方としては、数字を頂点に、書き換えるという動作を辺に、それに必要な魔力を辺の重みにすることで、各頂点から1への最短経路長を求めれば良いことになる。

これをダイクストラ法で解くとすると、1への距離を1以外の各頂点においてはINF、頂点1においては0で初期化し、1への距離が小さい順に各頂点において他の頂点の1への距離を更新していく。

あとは、各壁において必要な魔力の最小量を求めれば良い。

計算量は、ダイクストラ法においてはダイクストラ法 - Wikipedia によると O(E+VlogV)らしい(良くわかっていない)

もしそうであれば、今回は完全グラフなので、 E = V^{2}であり、計算量が O(V^{2})になるので、priority_queueを使わない方法( O(V^{2}))の方が良かったのだろう。

ただ、今回の制約では各壁を探索する O(HW)の方が大きくなる場合がほとんど なので、気にする必要は無い。

コード

#include <iostream>
#include <vector>
#include <queue>
using namespace std;
typedef vector<int> VI;
typedef vector<VI> VVI;
#define INF 1000

int main() {
  int h, w; cin >> h >> w;
  VVI c(10, VI(10));
  for (int i = 0; i < 10; i++) {
    for (int j = 0; j < 10; j++) cin >> c[i][j];
  }

  VI d(10, INF);
  d[1] = 0;
  auto comp = [&](int a, int b) { return d[a] > d[b]; };
  priority_queue<int, VI, decltype(comp)> que(comp);
  que.push(1);
  while (!que.empty()) {
    int v = que.top(); que.pop();
    for (int u = 0; u < 10; u++) {
      if (d[u] > d[v] + c[u][v]) {
        d[u] = d[v] + c[u][v];
        que.push(u);
      }
    }
  }

  int ans = 0;
  for (int i = 0; i < h; i++) {
    for (int j = 0; j < w; j++) {
      int a; cin >> a;
      if (a >= 0) ans += d[a];
    }
  }
  cout << ans << endl;
}

感想

計算量って難しい。