Algorithm (BFS)
- Construct a graph $G = (V, E)$ where element in $V$ is of the form $(r, c, \text{head})$, where $(r,c)$ represents the location of the snake’s tail and $\text{head}$ represents the head position.
- Note that two vertices $v_1$ and $v_2$ form an edge if and only if the snake can move from $v_1$ to $v_2$ in the way described in the problem.
- The the problem is reduced to finding a shortest path between $v_0 = (0, 0, \text{RIGHT})$ and $v_{\text{end}} = (R-1, C-2, \text{RIGHT})$, which could be solved using regular BFS.
Code
class Solution {
public:
int minimumMoves(vector<vector<int>>& grid) {
struct node_t {
const array<int, 2> tail;
const int head;
};
struct state_t {
mutable deque<node_t> Q;
mutable optional<int> D[100][100][2];
};
const int dr[4] = {1, -1, 0, 0};
const int dc[4] = {0, 0, 1, -1};
const int R = size(grid),
C = size(grid[0]),
RIGHT = 1,
DOWN = 0;
auto head_position = [](const node_t& node) -> array<int, 2> {
if (node.head == RIGHT)
return {node.tail[0], node.tail[1] + 1};
else
return {node.tail[0] + 1, node.tail[1]};
};
auto valid = [&](const array<int, 2>& pos) {
const auto [r, c] = pos;
return r >= 0 and r < R and c >= 0 and c < C and grid[r][c] != 1;
};
auto go_right = [&](const node_t& node) -> optional<node_t> {
const auto current_head = head_position(node);
const auto new_head = array<int, 2>{current_head[0], current_head[1] + 1};
const auto new_tail = array<int, 2>{node.tail[0], node.tail[1] + 1};
if (valid(new_head) and valid(new_tail))
return node_t{new_tail, node.head};
else
return {};
};
auto go_down = [&](const node_t& node) -> optional<node_t> {
const auto current_head = head_position(node);
const auto new_head = array<int, 2>{current_head[0] + 1, current_head[1]};
const auto new_tail = array<int, 2>{node.tail[0] + 1, node.tail[1]};
if (valid(new_head) and valid(new_tail))
return node_t{new_tail, node.head};
else
return {};
};
auto rot_clock = [&](const node_t& node) -> optional<node_t> {
if (go_down(node).has_value() and node.head == RIGHT)
return node_t{node.tail, DOWN};
else
return {};
};
auto rot_counter_clock = [&](const node_t& node) -> optional<node_t> {
if (go_right(node).has_value() and node.head == DOWN)
return node_t{node.tail, RIGHT};
return {};
};
const auto actions = vector<function<optional<node_t>(node_t)>> {
std::move(go_down),
std::move(go_right),
std::move(rot_clock),
std::move(rot_counter_clock)};
const auto solution = [&] {
const auto [Q, D] = state_t{deque<node_t>{node_t{{0, 0}, RIGHT}}, {}};
for (D[0][0][RIGHT] = 0; not empty(Q);) {
const auto [tail, head] = Q.front();
const auto [r, c] = tail;
Q.pop_front();
if (tail[0] == R - 1 and tail[1] == C - 2 and head == RIGHT)
return *D[r][c][head];
else
for (const auto f : actions)
if (const auto next = f({tail, head}); next.has_value()) {
const auto [ntail, nhead] = next.value();
const auto [nr, nc] = ntail;
if (not D[nr][nc][nhead]) {
D[nr][nc][nhead] = *D[r][c][head] + 1;
Q.emplace_back(node_t{ntail, nhead});
}
}
}
return -1;
}();
return solution;
}
};
233,最后匿名func直接执行,下次也这么写试试。