20250211 finished.

This commit is contained in:
jackfiled 2025-02-11 22:24:02 +08:00
parent 8033e2e9a4
commit f0e73f5c42
2 changed files with 369 additions and 0 deletions

View File

@ -478,3 +478,5 @@ mod p47_permutations_ii;
mod p59_spiral_matrix_ii;
mod p913_cat_and_mouse;
mod p1728_cat_and_mouse_ii;

View File

@ -0,0 +1,367 @@
/**
* [1728] Cat and Mouse II
*/
pub struct Solution {}
// submission codes start here
use std::collections::VecDeque;
const MOUSE_TURN: usize = 0;
const CAT_TURN: usize = 1;
const MAX_ROUND: i32 = 1000;
const DIRECTIONS: [(i32, i32); 4] = [(-1, 0), (1, 0), (0, -1), (0, 1)];
#[derive(Clone, Copy, Eq, PartialEq, Debug)]
enum Status {
DRAW,
MOUSE,
CAT,
}
#[derive(Copy, Clone, Debug)]
struct Result {
status: Status,
count: i32,
}
impl Result {
fn new() -> Self {
Self {
status: Status::DRAW,
count: 0,
}
}
fn new_with_status(status: Status) -> Self {
Self { status, count: 0 }
}
fn next(&self) -> Self {
Self {
status: self.status,
count: self.count + 1,
}
}
}
struct Gamer {
row: usize,
column: usize,
food: (usize, usize),
grid: Vec<Vec<char>>,
mouse_jump: i32,
cat_jump: i32,
degrees: Vec<Vec<Vec<i32>>>,
results: Vec<Vec<Vec<Result>>>,
}
impl Gamer {
// (Self, start_mouse_pos, start_cat_pos)
fn new(
grid: Vec<String>,
cat_jump: i32,
mouse_jump: i32,
) -> (Self, (usize, usize), (usize, usize)) {
let grid = grid
.into_iter()
.map(|x| x.chars().collect::<Vec<char>>())
.collect::<Vec<Vec<char>>>();
let (row, column) = (grid.len(), grid[0].len());
let (mut start_mouse, mut start_cat, mut food) = ((0, 0), (0, 0), (0, 0));
for i in 0..row {
for j in 0..column {
match grid[i][j] {
'M' => start_mouse = (i, j),
'C' => start_cat = (i, j),
'F' => food = (i, j),
_ => {}
}
}
}
let total = row * column;
let mut degrees = vec![vec![vec![0; 2]; total]; total];
let mut results = vec![vec![vec![Result::new(); 2]; total]; total];
// 计算每个状态的度
for mouse_i in 0..row {
for mouse_j in 0..column {
let mouse_state = mouse_i * column + mouse_j;
if grid[mouse_i][mouse_j] == '#' {
continue;
}
for cat_i in 0..row {
for cat_j in 0..column {
let cat_state = cat_i * column + cat_j;
if grid[cat_i][cat_j] == '#' {
continue;
}
degrees[mouse_state][cat_state][MOUSE_TURN] += 1;
degrees[mouse_state][cat_state][CAT_TURN] += 1;
for dir in DIRECTIONS.iter() {
let (mouse_i, mouse_j) = (mouse_i as i32, mouse_j as i32);
// 让老鼠先跳
for jump in 1..=mouse_jump {
let (i, j) = (mouse_i + jump * dir.0, mouse_j + jump * dir.1);
if i >= 0 && i < row as i32 && j >= 0 && j < column as i32 {
let (i, j) = (i as usize, j as usize);
if grid[i][j] == '#' {
break;
}
degrees[i * column + j][cat_state][MOUSE_TURN] += 1;
}
}
let (cat_i, cat_j) = (cat_i as i32, cat_j as i32);
// 让猫跳
for jump in 1..=cat_jump {
let (i, j) = (cat_i + jump * dir.0, cat_j + jump * dir.1);
if i >= 0 && i < row as i32 && j >= 0 && j < column as i32 {
let (i, j) = (i as usize, j as usize);
if grid[i][j] == '#' {
break;
}
degrees[mouse_state][i * column + j][CAT_TURN] += 1;
}
}
}
}
}
}
}
(
Self {
row,
column,
food,
grid,
degrees,
results,
mouse_jump,
cat_jump,
},
start_mouse,
start_cat,
)
}
fn get_state(&self, (x, y): (usize, usize)) -> usize {
self.column * x + y
}
fn search(&mut self, start_mouse: (usize, usize), start_cat: (usize, usize)) -> bool {
let mut queue = VecDeque::new();
// 猫和老鼠在同一位置
// 猫必胜
for i in 0..self.row {
for j in 0..self.column {
if self.grid[i][j] == '#' {
continue;
}
let state = self.get_state((i, j));
self.results[state][state][MOUSE_TURN] = Result::new_with_status(Status::CAT);
self.results[state][state][CAT_TURN] = Result::new_with_status(Status::CAT);
queue.push_back((state, state, MOUSE_TURN));
queue.push_back((state, state, CAT_TURN));
}
}
// 猫和食物在同一位置
// 猫必胜
let food_state = self.get_state(self.food);
for i in 0..self.row {
for j in 0..self.column {
if self.grid[i][j] == '#' || (i, j) == self.food {
continue;
}
let mouse_state = self.get_state((i, j));
self.results[mouse_state][food_state][MOUSE_TURN] =
Result::new_with_status(Status::CAT);
self.results[mouse_state][food_state][CAT_TURN] =
Result::new_with_status(Status::CAT);
queue.push_back((mouse_state, food_state, MOUSE_TURN));
queue.push_back((mouse_state, food_state, CAT_TURN));
}
}
// 老鼠和食物在同一位置且猫不在
// 老鼠必胜
for i in 0..self.row {
for j in 0..self.column {
if self.grid[i][j] == '#' || (i, j) == self.food {
continue;
}
let cat_state = self.get_state((i, j));
self.results[food_state][cat_state][MOUSE_TURN] =
Result::new_with_status(Status::MOUSE);
self.results[food_state][cat_state][CAT_TURN] =
Result::new_with_status(Status::MOUSE);
queue.push_back((food_state, cat_state, MOUSE_TURN));
queue.push_back((food_state, cat_state, CAT_TURN));
}
}
// 开始拓扑排序
while let Some((mouse_state, cat_state, turn)) = queue.pop_front() {
let result = self.results[mouse_state][cat_state][turn];
for (previous_mouse_state, previous_cat_state, previous_turn) in
self.get_previous_states(mouse_state, cat_state, turn)
{
if self.results[previous_mouse_state][previous_cat_state][previous_turn].status
== Status::DRAW
{
if (result.status == Status::MOUSE && previous_turn == MOUSE_TURN)
|| (result.status == Status::CAT && previous_turn == CAT_TURN)
{
self.results[previous_mouse_state][previous_cat_state][previous_turn] =
result.next();
queue.push_back((previous_mouse_state, previous_cat_state, previous_turn));
} else {
self.degrees[previous_mouse_state][previous_cat_state][previous_turn] -= 1;
if self.degrees[previous_mouse_state][previous_cat_state][previous_turn]
== 0
{
self.results[previous_mouse_state][previous_cat_state][previous_turn] =
Result {
status: if previous_turn == MOUSE_TURN {
Status::CAT
} else {
Status::MOUSE
},
count: result.count + 1,
};
queue.push_back((
previous_mouse_state,
previous_cat_state,
previous_turn,
));
}
}
}
}
}
let result =
&self.results[self.get_state(start_mouse)][self.get_state(start_cat)][MOUSE_TURN];
result.status == Status::MOUSE && result.count <= MAX_ROUND
}
// (mouse, cat, turn)
fn get_previous_states(
&self,
mouse_state: usize,
cat_state: usize,
turn: usize,
) -> Vec<(usize, usize, usize)> {
let mut states = vec![];
let (mouse_x, mouse_y) = (
(mouse_state / self.column) as i32,
(mouse_state % self.column) as i32,
);
let (cat_x, cat_y) = (
(cat_state / self.column) as i32,
(cat_state % self.column) as i32,
);
let previous_turn = if turn == MOUSE_TURN {
CAT_TURN
} else {
MOUSE_TURN
};
let jump = if previous_turn == MOUSE_TURN {
self.mouse_jump
} else {
self.cat_jump
};
let (start_x, start_y) = if previous_turn == MOUSE_TURN {
(mouse_x, mouse_y)
} else {
(cat_x, cat_y)
};
states.push((mouse_state, cat_state, previous_turn));
for dir in DIRECTIONS.iter() {
for j in 1..=jump {
let (x, y) = (start_x + j * dir.0, start_y + j * dir.1);
if x >= 0 && x < self.row as i32 && y >= 0 && y < self.column as i32 {
let (x, y) = (x as usize, y as usize);
if self.grid[x][y] == '#' {
break;
}
let previous_mouse = if previous_turn == MOUSE_TURN {
(x, y)
} else {
(mouse_x as usize, mouse_y as usize)
};
let previous_cat = if previous_turn == CAT_TURN {
(x, y)
} else {
(cat_x as usize, cat_y as usize)
};
states.push((
self.get_state(previous_mouse),
self.get_state(previous_cat),
previous_turn,
));
}
}
}
states
}
}
impl Solution {
pub fn can_mouse_win(grid: Vec<String>, cat_jump: i32, mouse_jump: i32) -> bool {
let (mut gamer, start_mouse, start_cat) = Gamer::new(grid, cat_jump, mouse_jump);
gamer.search(start_mouse, start_cat)
}
}
// submission codes end
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_1728() {
assert!(!Solution::can_mouse_win(
vec_string!("C...#", "...#F", "....#", "M...."),
2,
5
));
assert!(Solution::can_mouse_win(vec_string!("M.C...F"), 1, 4));
assert!(Solution::can_mouse_win(
vec_string!("####F", "#C...", "M...."),
1,
2
));
}
}