AtCoder が提供しているABC(AtCoder Beginner Contest)020 C問題をC++で解いてみました。ABC020は、2015年3月21日21:00に実施されました。
AtCoder の紹介はこちらに、プログラミングの方針はこちらに記事があります。
C問題 壁抜け(Difficulty : 1477(参考値))
問題の詳細は、リンク先をご覧ください。
解の二分探索を用いて、黒マスを通過する時間を求めます。ゴールに間に合うかの判断にはダイクストラ法を使います。AtCoder Problems による Difficulty は 1477(参考値)でした。
解答案
C++ プログラム例(ABC020C)
黒マスを通るための時間 $x$ を「解の二分探索」で求めます(78ー87行目)。
判定関数 is_OK
をラムダ式で定義しました(39-76行目)。
- 始点からダイクストラ法を用いて、他のグリッドへの距離を求めます。
- 白マスの場合は距離1、黒マスの場合は引数で与えられた
mid
とします。
- 白マスの場合は距離1、黒マスの場合は引数で与えられた
- 終点までの距離が
t
以下であれば真を、そうでなければ偽を返します(75行目)。
実装は少し長くなりました。以下が、C++プログラムです。
#include <bits/stdc++.h>
using namespace std;
typedef long long int ll;
typedef pair<ll, pair<int, int>> PP;
#define INF (1LL << 60)
int main()
{
int h, w;
ll t;
cin >> h >> w >> t;
vector<string> s(h);
for (int i = 0; i < h; ++i) {
cin >> s[i];
}
int si = -1;
int sj = -1;
int gi = -1;
int gj = -1;
for (int i = 0; i < h; ++i) {
for (int j = 0; j < w; ++j) {
if (s[i][j] == 'S') {
si = i;
sj = j;
s[i][j] = '.';
} else if (s[i][j] == 'G') {
gi = i;
gj = j;
s[i][j] = '.';
}
}
}
int di[4] = {1, 0, -1, 0};
int dj[4] = {0, 1, 0, -1};
auto is_OK = [&](ll mid) -> bool {
priority_queue<PP, vector<PP>, greater<PP>> que;
vector<vector<ll>> dist(h, vector<ll>(w, INF));
que.push(make_pair(0, make_pair(si, sj)));
dist[si][sj] = 0;
while (!que.empty()) {
auto pp = que.top();
que.pop();
int now_i = pp.second.first;
int now_j = pp.second.second;
if (dist[now_i][now_j] < pp.first) {
continue;
}
for (int d = 0; d < 4; ++d) {
int next_i = now_i + di[d];
int next_j = now_j + dj[d];
if ((next_i < 0) || (h <= next_i)) {
continue;
}
if ((next_j < 0) || (w <= next_j)) {
continue;
}
ll weight;
if (s[next_i][next_j] == '#') {
weight = mid;
} else {
weight = 1;
}
if (dist[next_i][next_j] > dist[now_i][now_j] + weight) {
dist[next_i][next_j] = dist[now_i][now_j] + weight;
que.push(make_pair(dist[next_i][next_j],
make_pair(next_i, next_j)));
}
}
}
return dist[gi][gj] <= t;
};
ll ng = 1000000001;
ll ok = 1;
while (abs(ok - ng) > 1) {
ll mid = (ok + ng) / 2;
if (is_OK(mid)) {
ok = mid;
} else {
ng = mid;
}
}
cout << ok << endl;
return 0;
}
AC(Accepted=正しいプログラム)と判定されました。
最後に
解の二分探索を使う問題ですが、判定関数でダイクストラ法を使う必要がありました。学習効果の高い問題だと判断し、記事で紹介しました。
引き続き ABC の問題を紹介していきます。