ABC088 D - Grid Repairing

Calendar Clock iconCalendar Clock icon

atcoder

目次

# 問題

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

# 入力

HWH W
s0,0s0,1...s0,W1s_{0,0} s_{0,1} ... s_{0,W-1}
...
sH1,0sH1,1...sH1,W1s_{H-1,0} s_{H-1,1} ... s_{H-1,W-1}.

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

# 出力

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

# 解説

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

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

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

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

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

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

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

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

# 計算量

O(HW)O(H W)

# 解答

// C++ 14
#include <iostream>
#include <string>
#include <vector>
#include <list>
#include <algorithm>
#include <queue>
#include <stack>
#include <set>
#include <map>
#include <unordered_map>
#include <math.h>

#define ll long long
#define Int int
#define loop(x, start, end) for(Int x = start; x < end; x++)
#define loopdown(x, start, end) for(int x = start; x > end; x--)
#define rep(n) for(int x = 0; x < n; x++)
#define span(a,x,y) a.begin()+x,a.begin()+y
#define span_all(a) a.begin(),a.end()
#define len(x) (x.size())
#define last(x) (*(x.end()-1))

using namespace std;

#define EPS 0.0000000001
#define fequals(a,b) (fabs((a) - (b)) < EPS)

class Vector2 {
public:
  double x, y;
  
  Vector2(double x = 0, double y = 0): x(x), y(y) {}
  
  Vector2 operator + (const Vector2 v) const { return Vector2(x + v.x, y + v.y); }
  Vector2 operator - (const Vector2 v) const { return Vector2(x - v.x, y - v.y); }
  Vector2 operator * (const double k) const { return Vector2(x * k, y * k); }
  Vector2 operator / (const double k) const { return Vector2(x / k, y / k); }
  
  double length() { return sqrt(norm()); }
  double norm() { return x * x + y * y; }
  double dot (Vector2 const v) { return x * v.x + y * v.y; }
  double cross (Vector2 const v) { return x * v.y - y * v.x; }
  
  bool parallel(Vector2 &other) {
    return fequals(0, cross(other));
  }
  
  bool orthogonal(Vector2 &other) {
    return fequals(0, dot(other));
  }
  
  bool operator < (const Vector2 &v) {
    return x != v.x ? x < v.x : y < v.y;
  }
  
  bool operator == (const Vector2 &v) {
    return fabs(x - v.x) < EPS && fabs(y - v.y) < EPS;
  }
};

ostream & operator << (ostream & out, Vector2 const & v) { 
  out<< "Vector2(" << v.x << ", " << v.y << ')';
  return out;
}

istream & operator >> (istream & in, Vector2 & v) { 
  double x, y;
  in >> x;
  in >> y;
  v.x = x;
  v.y = y;
  return in;
}
#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;
}

リモートフリーランス。ウェブサービス、スマホアプリエンジニア。
東アジアを拠点に世界を移動しながら活動してます!

お仕事のご依頼・お問い合わせはこちら

コメント