Official
J - 右折/Turn Right Editorial
by
J - 右折/Turn Right Editorial
by
MMNMM
いるマスとその向きの組を状態とし、マス \((2,2)\) で \(4\) 方向それぞれを向いている状態から探索を行うことを考えます(高橋くんのありえる動きを逆向きに辿る形になります)。
ある状態から移動できる状態は、次の(高々) \(2\) つです。
- 後ろのマスが壁でなければ、後ろに下がる
- 左のマスが壁ならば、左を向く
あとは、これを使って幅優先探索や深さ優先探索を行えばよいです。
時間計算量や空間計算量は \(O(N ^ 2)\) などとなり、十分高速です。
実装例は以下のようになります。
#include <iostream>
#include <vector>
#include <queue>
#include <tuple>
#include <utility>
int main() {
using namespace std;
unsigned N;
cin >> N;
vector wall_cell(N, vector<bool>(N, true));
for (unsigned i{}; i < N; ++i) {
string S;
cin >> S;
for (unsigned j{}; j < N; ++j)
if (S[j] == '.')
wall_cell[i][j] = false;
}
// 向きを表す列挙型
enum class direction : char {
up, left, down, right
};
// 向きから左を向いたときの向きを求める関数
constexpr auto turn_left{[](const direction d) {
switch (d) {
case direction::up:
return direction::left;
case direction::left:
return direction::down;
case direction::down:
return direction::right;
case direction::right:
return direction::up;
}
}};
// 今いるマスと向きから後ろに進んだマスを返す関数
constexpr auto go_back{[](const unsigned x, const unsigned y, const direction d) -> pair<unsigned, unsigned> {
switch (d) {
case direction::up:
return {x + 1, y};
case direction::left:
return {x, y + 1};
case direction::down:
return {x - 1, y};
case direction::right:
return {x, y - 1};
}
}};
// 今いるマスと向きから左隣のマスを返す関数
constexpr auto go_left{[](const unsigned x, const unsigned y, const direction d) -> pair<unsigned, unsigned> {
switch (d) {
case direction::up:
return {x, y - 1};
case direction::left:
return {x + 1, y};
case direction::down:
return {x, y + 1};
case direction::right:
return {x - 1, y};
}
}};
vector visited(N, vector<char>(N));
// (今いるマス, 向き) の組を探索済みか判定する関数
const auto is_visited{[&visited](const unsigned x, const unsigned y, const direction d) -> bool {
return 1 & (visited[x][y] >> static_cast<char>(d));
}};
// (今いるマス, 向き) の組を探索済みに更新する関数
const auto visit{[&visited](const unsigned x, const unsigned y, const direction d) -> void {
visited[x][y] |= 1 << static_cast<char>(d);
}};
queue<tuple<unsigned, unsigned, direction>> bfs;
// ゴールのマスから探索を始める
bfs.emplace(1, 1, direction::up);
bfs.emplace(1, 1, direction::left);
bfs.emplace(1, 1, direction::down);
bfs.emplace(1, 1, direction::right);
while (!empty(bfs)) {
const auto [x, y, d]{bfs.front()};
bfs.pop();
{
// ひとつ戻れるなら、後ろを探索
const auto &[px, py]{go_back(x, y, d)};
if (!wall_cell[px][py] && !is_visited(px, py, d)) {
visit(px, py, d);
bfs.emplace(px, py, d);
}
}
{
// 左が壁なら、左に向いた状態を探索
const auto pd{turn_left(d)};
const auto [wx, wy]{go_left(x, y, d)};
if (wall_cell[wx][wy] && !is_visited(x, y, pd)) {
visit(x, y, pd);
bfs.emplace(x, y, pd);
}
}
}
unsigned ans{};
for (unsigned i{}; i < N; ++i)
for (unsigned j{}; j < N; ++j)
// どれかの向きで探索済みなら、そのマスから到達可能
ans += [&is_visited](unsigned i, unsigned j) {
if (is_visited(i, j, direction::up))
return true;
if (is_visited(i, j, direction::left))
return true;
if (is_visited(i, j, direction::down))
return true;
return is_visited(i, j, direction::right);
}(i, j);
cout << ans << endl;
return 0;
}
posted:
last update:
