C - Connect 6 解説
by
mechanicalpenciI
まず、黒く塗られたマスが縦、横、ななめのいずれかの向きに \(6\) つ以上連続しているとは、縦、横、ななめのいずれかの向きに連続した \(6\) マスであって、その \(6\) マスがすべて黒く塗られているものが存在するという事です。
では、あと高々 \(2\) マス黒く塗ることで黒く塗られたマスが縦、横、ななめのいずれかの向きに \(6\) つ以上連続するようにできるとは、どのような状況でしょうか?これは、縦、横、ななめのいずれかの向きに連続した \(6\) マスであって、その \(6\) マスのうち \(4\) マス以上がすべて黒く塗られているものが存在するという事です。もしこのようなものが存在すればその \(6\) マスの黒く塗られていない残りの高々 \(2\) マスを塗れば条件をみたすようにできます。逆に存在しないならば、どのように \(2\) マスを塗っても達成できません。これは条件をみたすマス目から高々 \(2\) 個の黒く塗られたマスを白く塗ることを考えれば明らかです。
ここで、それぞれの方向に \(6\) つ連続したマス目はいくつあるでしょうか? 縦方向は各列 \((N-5)\) 個でマス目の中に全部で \(N(N-5)\) 個、横方向も行と列を入れ替えて全部で \(N(N-5)\) 個です。ななめ方向は \(6\) 行 \(6\) 列の正方形の取り方で \((N-5)^2\) 通り, 対角線の取り方で \(2\) 通りです。よって、調べなければならないマスの数は全部合わせてのべ高々 \(6\times\bigl\{ 2N(N-5)+2(N-5)^2 \bigr\}<24N^2\) 個程度です。\(N\leq 1000\) ですからこれを全探索しても十分高速です。よって、この問題を解く事が出来ました。
実装時の注意としては、調べる範囲やマスの指定、また、マス目の外側を塗らないと \(6\) 連続にできないパターン(下図のようなもの)に注意してください。
....#.
...#..
..#...
.#....
#.....
......
c++による実装例 :
#include <bits/stdc++.h>
using namespace std;
#define rep(i, n) for(int i = 0; i < n; ++i)
int main(void) {
int n, cnt;
bool ans = false;
vector<string>a;
string str;
cin >> n;
for (int i = 0; i < n; i++){
cin >> str;
a.push_back(str);
}
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if ((i + 5) < n) {
cnt = 0;
for (int k = 0; k < 6; k++)if (a[i + k][j] == '#')cnt++;
if (cnt >= 4)ans = true;
}
if ((j + 5) < n) {
cnt = 0;
for (int k = 0; k < 6; k++)if (a[i][j + k] == '#')cnt++;
if (cnt >= 4)ans = true;
}
if (((i + 5) < n) && ((j + 5) < n)) {
cnt = 0;
for (int k = 0; k < 6; k++)if (a[i + k][j + k] == '#')cnt++;
if (cnt >= 4)ans = true;
}
if ((0 <= (i - 5)) && ((j + 5) < n)) {
cnt = 0;
for (int k = 0; k < 6; k++)if (a[i - k][j + k] == '#')cnt++;
if (cnt >= 4)ans = true;
}
}
}
if (ans)cout << "Yes" << endl;
else cout << "No" << endl;
return 0;
}
ちなみに、 \(6\)(黒マスを連続させたい数)がより一般に \(K\) であった場合この解法では \( O(KN^2)\) がかかってしまいますが、それぞれの方向について #
の数を累積和のような形で数えてあげることで \(K\) の値に依らず \(O(N^2)\) で解くことができます。
投稿日時:
最終更新: