幅優先探索 abc088 d atcoder

ABC088 D - Grid Repairing

ABC088 D - Grid Repairing
Feb. 2, 2020, 1:52 p.m.

目次

問題

https://atcoder.jp/contests/abc088/tasks/abc088_d

入力

$H W$
$s_{0,0} s_{0,1} ... s_{0,W-1}$
...
$s_{H-1,0} s_{H-1,1} ... s_{H-1,W-1}$.

  • $H$ - ボードの高さ
  • $W$ - ボードの幅
  • $s_{x,y}$ - 座標$(x, y)$のマス目. .または#
  • $s_{0,0}, s_{H-1,W-1}$. - ともに.で、$(0, 0)$がスタート、$(H-1, W-1)$がゴール.

出力

事前に任意の数だけ.#に変更してスタートすることが出来ます.
.のみを通ってゴールまで行く時、最大で何個の.#に変えることが出来るかを答える問題です.
ゴールに到達出来ない場合は-1と答えます.

解説

一見複雑そうですが、よく読むと事前に変えられるのは.のみです.
.#に変えてもゴールまでの障害物が増えるだけです.
なので実は1マスも変更しない状態がゴールまでの最短経路となっています.
最短経路を変えないように最大何マス変えられるかというのは問題です.
なので最短経路で通ったマスを邪魔しないように、それ以外のマスを変更しそのマスを数えれば良いです.

まず最短経路の求め方ですが、これは基本的な幅優先探索で求められます.
スタート地点から4近傍で移動できるマスすべてに移動していきます.
幅優先探索なので、最初にゴールに到達した時それが最短経路であることが保証されています.
今回は距離を答えるわけではなく、通ったマスの座標が重要なのでそれをすべてベクターで保存しておきましょう.

ここまでの計算量は、 すべてのマスを1回ずつ訪問するので$O(H W)$です.

次にもともとのボードで最短経路で通った座標を.から#に更新します.
この計算量は経路の長さに依存するので最大でも約$O(H W)$です.

最後にすべてのマスをチェックして.のまま残っているものを数え上げてそれが答えです.
なぜなら#となっているものは以下のどちらかだからです.

  • 1) 最初から#だったから変更できない
  • 2) 最短経路で使用するマスなので変更できない

この計算量は全マスを1回ずつチェックするので$O(H W)$です.

幅優先探索の手順の時にゴールが見つからなければ-1と出力します.

計算量

$$
O(H W)
$$

解答

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
#define MAX_H 50
#define INFTY (MAX_H*MAX_H)

Int H, W, N, sx, sy, gx, gy;
char c_;
vector<bool> M(MAX_H * MAX_H, true);
vector<bool> done(MAX_H * MAX_H, false);

void input() {
  cin >> H >> W;
  gx = W -1, gy = H - 1;
  sx = sy = 0;
  loop(h,0,H) {
    loop(w,0,W) {
      cin >> c_;
      if (c_ == '#') M[h*W+w] = false;
    }
  }
  M.resize(H * W);
  done.resize(H * W);
}

vector<Vector2> bfs(Int sx, Int sy) {
  queue<vector<Vector2> > Q;
  vector<Vector2> path;
  path.push_back(Vector2(sx, sy));
  Q.push(path);
  done[sy*W+sx] = true;

  while (Q.size()) {
    vector<Vector2> us = Q.front(); Q.pop();
    Vector2 u = last(us);
    loop(dx,-1,2) {
      loop(dy,-1,2) {
        if (dx != 0 && dy != 0) continue;
        Vector2 next = u + Vector2(dx, dy);
        if (next.x < 0 || next.x >= W || next.y < 0 || next.y >= H) continue;
        if (!M.at(next.y*W+next.x)) continue;
        if (done.at(next.y*W+next.x)) continue;
        vector<Vector2> copy = us;
        copy.push_back(next);
        done[next.y*W+next.x] = true;
        if (next.x == gx && next.y == gy) {
          return copy;
        }
        Q.push(copy);
      }
    }
  }

  vector<Vector2> empty;
  return empty;
}

void solve() {
  vector<Vector2> path = bfs(sx, sy);
  if (!path.size()) {
    cout << -1 << endl;
    return;
  }

  for (auto v: path) {
    M[v.y*W+v.x] = false;
  }

  Int counter = 0;
  loop(h,0,H) {
    loop(w,0,W) {
      if (M[h*W+w]) counter++;
    }
  }

  cout << counter << endl;
}

int main(void) {
  input();
  solve();
  return 0;
}