AtCoder

ABC020 C問題(壁抜け)を解く

AtCoder_ABC020_C

AtCoder が提供しているABC(AtCoder Beginner Contest)020 C問題をC++で解いてみました。ABC020は、2015年3月21日21:00に実施されました。

AtCoder の紹介はこちらに、プログラミングの方針はこちらに記事があります。

C問題 壁抜け(Difficulty : 1477(参考値))

問題の詳細は、リンク先をご覧ください。

ABC020 C問題 壁抜け

解の二分探索を用いて、黒マスを通過する時間を求めます。ゴールに間に合うかの判断にはダイクストラ法を使います。AtCoder Problems による Difficulty は 1477(参考値)でした。

解答案

C++ プログラム例(ABC020C)

黒マスを通るための時間 $x$ を「解の二分探索」で求めます(78ー87行目)。

判定関数 is_OK をラムダ式で定義しました(39-76行目)。

  • 始点からダイクストラ法を用いて、他のグリッドへの距離を求めます。
    • 白マスの場合は距離1、黒マスの場合は引数で与えられた mid とします。
  • 終点までの距離が 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 の問題を紹介していきます。

COMMENT

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA