AtCoder

ABC351 D問題(Grid and Magnet)を解く

AtCoder_ABC351_D

AtCoder が提供しているABC(AtCoder Beginner Contest)351 のD問題をC++とPythonで解いてみました。ABC351は、2024年4月27日21:00に実施されました。

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

D問題 Grid and Magnet(Difficulty : 974)

問題はリンク先をご覧ください。

ABC351 D問題 Grid and Magnet

到達すると動けなくなるマスがありますが、連結成分を求める問題です。AtCoder Problems による Difficulty は 974 でした。

解答案

すべてのマスについて、連結しているマスの個数をカウントします。注意は以下です。

  • 磁石が置かれているマスの隣ではないマスは、いずれかの連結成分に所属します。お互いに行き来できるマスは同じ連結成分と見なされるためです。
  • 一方、磁石が置かれているマスの隣のマスは、複数の連結成分に所属する場合があります。これは、このマスを連結成分として共有していても、行き来できないマスがありえるためです。

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

連結成分の個数は、DFS で求めることが多いですが、今回は BFS(キュー)を使用しました。磁石の隣のマスは、’o’ に書き換えておきます(15ー29行目)。

本体部分のコード(31ー76行目)は、少し長くなりました。補足します。

  • すべてのマスについて、連結成分の個数を調べます。どの連結成分に属するかは、配列 area で管理します。
  • 磁石の隣のマスが始点の場合、連結成分のマスの個数として1を格納します(40ー43行目)。
  • なにも置かれていないマスは、どの連結成分でもない場合に調べます(44行目のif文)。
  • BFS で同じ連結成分に属するマスをキューに格納していきます。ただし、磁石の隣のマスの場合は、そこから動けないため、これ以上キューに格納しません(57ー59行目)。
  • 52ー73行目は、BFSの定番コードをベースにしています。
  • 連結成分のマスの個数は、配列 result に格納します(74行目)。

最後に配列 result に格納されている要素の最大値を出力して終了します。以下が、C++プログラムとなります。

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

int main()
{
	int h, w;
	cin >> h >> w;
	vector<string> s(h);
	for (int i = 0; i < h; ++i) {
		cin >> s[i];
	}

	int di[4] = {1, 0, -1,  0};
	int dj[4] = {0, 1,  0, -1};
	for (int i = 0; i < h; ++i) {
		for (int j = 0; j < w; ++j) {
			if (s[i][j] == '#') {
				for (int d = 0; d < 4; ++d) {
					int ti = i + di[d];
					int tj = j + dj[d];
					if ((0 <= ti)&&(ti < h)
					  &&(0 <= tj)&&(tj < w)
					  &&(s[ti][tj] == '.')) {
						s[ti][tj] = 'o';
					}
				}
			}
		}
	}

	vector<int> result;
	queue<pair<int, int>> que;
	vector<vector<int>> area(h, vector<int>(w, -1));
	int area_no = 0;
	for (int i = 0; i < h; ++i) {
		for (int j = 0; j < w; ++j) {
			if (s[i][j] == '#') {
				continue;
			}
			if (s[i][j] == 'o') {
				result.push_back(1);
				continue;
			}
			if ((s[i][j] == '.')&&(area[i][j] != -1)) {
				continue;
			}
			++area_no;
			int this_area = 0;
			que.push(make_pair(i, j));
			area[i][j] = area_no;
			++this_area;
			while (!que.empty()) {
				pair<int, int> now = que.front();
				que.pop();
				int now_i = now.first;
				int now_j = now.second;
				if (s[now_i][now_j] == 'o') {
					continue;
				}

				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)
					  &&(area[next_i][next_j] != area_no)
					  &&(s[next_i][next_j] != '#')) {
						que.push(make_pair(next_i, next_j));
						area[next_i][next_j] = area_no;
						++this_area;
					}
				}
			}
			result.push_back(this_area);
		}
	}

	int max_result = 0;
	for (auto e : result) {
		max_result = max(max_result, e);
	}
	cout << max_result << endl;

	return 0;
}

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

Python プログラム例(ABC351D)

Python は文字列の変数を書き換えることができないため、mark というリストを定義して使います。基本的な考え方は、C++版と同じです。以下となります。

"""AtCoder Beginner Contest 351 D"""
from collections import deque

h, w = map(int, input().split())
s = [input() for i in range(h)]

mark = [['.' for _ in range(w)] for _ in range(h)]
di = [1, 0, -1,  0]
dj = [0, 1,  0, -1]
for i in range(h):
    for j in range(w):
        if s[i][j] == '#':
            mark[i][j] = '#'
            for d in range(4):
                ti = i + di[d]
                tj = j + dj[d]
                if 0 <= ti < h and 0 <= tj < w and mark[ti][tj] == '.':
                    mark[ti][tj] = 'o'

result = []
que = deque()
area = [[-1 for _ in range(w)] for _ in range(h)]
area_no = 0
for i in range(h):
    for j in range(w):
        if mark[i][j] == '#':
            continue
        if mark[i][j] == 'o':
            result.append(1)
            continue
        if mark[i][j] == '.' and area[i][j] != -1:
            continue
        area_no += 1
        this_area = 0
        que.append((i, j))
        area[i][j] = area_no
        this_area += 1
        while que:
            now_i, now_j = que.popleft()
            if mark[now_i][now_j] == 'o':
                continue

            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 \
                   area[next_i][next_j] != area_no and \
                   mark[next_i][next_j] != '#':
                    que.append((next_i, next_j))
                    area[next_i][next_j] = area_no
                    this_area += 1
        result.append(this_area)

print(max(result))

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

最後に

よくあるグリッドの問題に磁石の設定を加えた問題でした。コンテスト中に解くことができて、うれしく感じました。

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

COMMENT

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

CAPTCHA