AtCoder

ABC384 E問題(Takahashi is Slime 2)を解く

AtCoder_ABC384_E

AtCoder が提供しているABC(AtCoder Beginner Contest)384 E問題をC++とPythonで解いてみました。ABC384は、2024年12月14日21:00に実施されました。

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

E問題 Takahashi is Slime 2(Difficulty : 1002)

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

ABC384 E問題 Takahashi is Slime 2

優先度付きキューを用いて吸収するマスを効率的に選択します。AtCoder Problems による Difficulty は 1002 でした。

解答案

高橋くんがいるマス $(P, Q)$ の上下左右のマスが吸収するマスの候補になります。あるマスを吸収できたら、その上下左右のマスも吸収するマスの候補となります。

強さが小さいマスから吸収すればよいため、候補となるマスを優先度付きキューに格納すれば効率よく処理できます。

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

プログラムの補足です。

  • すでにキューに格納されたか2次元配列 queued で管理します。
  • あるマスの上下左右をキューに格納するラムダ式 push_queue を定義します。
  • $(P, Q)$ の上下左右のマスをキューに格納します(44行目)。
  • キューからマスを取り出します。
    • その強さが高橋くんの $\frac{1}{X}$ 倍以上であれば、候補となるマスが無くなるため、解を出力します 。
      • 割り算の結果を double で計算しました。誤差の影響を考慮して DBL_EPSILON を加えて評価しています(51行目)。
    • その強さが $\frac{1}{X}$ 倍未満であれば、その上下左右のマスをキューに格納して、高橋くんの強さを更新します(54、55行目) 。

以下が、C++プログラムです。

#include <bits/stdc++.h>
using namespace std;

typedef long long int ll;
typedef pair<ll, pair<int, int>> PP;

int main()
{
	int h, w;
	ll x;
	cin >> h >> w >> x;
	int p, q;
	cin >> p >> q;
	--p;
	--q;
	vector<vector<ll>> s(h, vector<ll>(w));
	for (int i = 0; i < h; ++i) {
		for (int j = 0; j < w; ++j) {
			cin >> s[i][j];
		}
	}

	int di[4] = {1, 0, -1, 0};
	int dj[4] = {0, 1, 0, -1};
	vector<vector<bool>> queued(h, vector<bool>(w, false));
	priority_queue<PP, vector<PP>, greater<PP>> que;

	ll result = s[p][q];
	queued[p][q] = true;

	auto push_queue = [&](int now_i, int now_j) -> void {
		for (int d = 0; d < 4; ++d) {
			int next_i = now_i + di[d];
			int next_j = now_j + dj[d];
			if ((0 <= next_i) && (next_i < h) && (0 <= next_j) &&
				(next_j < w) && !queued[next_i][next_j]) {
				que.push(
					make_pair(s[next_i][next_j], (make_pair(next_i, next_j))));
				queued[next_i][next_j] = true;
			}
		}
	};

	push_queue(p, q);

	while (!que.empty()) {
		PP now = que.top();
		que.pop();
		int now_i = now.second.first;
		int now_j = now.second.second;
		if ((double)now.first + DBL_EPSILON >= (double)result / (double)x) {
			break;
		}
		result += now.first;
		push_queue(now_i, now_j);
	}

	cout << result << endl;

	return 0;
}

高橋くんの強さとマスの強さを評価する際、整数の切り上げ結果と比較する方法も有効です。具体的には、51行目は以下のように記載します。

		if (now.first >= (result + x - 1) / x) {
			break;
		}

どちらも AC(Accepted=正しいプログラム)と判定されました。

Python プログラム例(ABC384E)

Python版も基本的な考え方は同じです。優先度付きキューは、heapq を使いました。以下がプログラムです。

"""AtCoder Beginner Contest 384 E"""
import heapq


def push_queue(now_i, now_j):
    global h, w, di, dj, queued, que

    for d in range(4):
        next_i = now_i + di[d]
        next_j = now_j + dj[d]
        if 0 <= next_i < h and 0 <= next_j < w and not queued[next_i][next_j]:
            heapq.heappush(que, (s[next_i][next_j], (next_i, next_j)))
            queued[next_i][next_j] = True


h, w, x = map(int, input().split())
p, q = map(int, input().split())
p -= 1
q -= 1
s = [[0 for _ in range(w)] for _ in range(h)]
for i in range(h):
    s[i] = list(map(int, input().split()))

di = [1, 0, -1,  0]
dj = [0, 1,  0, -1]
queued = [[False for _ in range(w)] for _ in range(h)]
que = []
heapq.heapify(que)

result = s[p][q]
queued[p][q] = True

push_queue(p, q)

while que:
    now = heapq.heappop(que)
    now_i = now[1][0]
    now_j = now[1][1]
    if now[0] >= (result + x - 1) // x:
        break
    result += now[0]
    push_queue(now_i, now_j)

print(result)

こちらも「AC」と判定されました。

最後に

コンテスト中はBFSを用いた処理を試みましたが、WA(Wrong Answer)となりました。優先度付きキューを利用する方法は思いついたものの、時間切れとなってしまいました。勉強になりました。

引き続き ABC の問題を紹介していきます。

COMMENT

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

CAPTCHA