leetcode

Leetcode problem solutions
git clone https://git.sinitax.com/sinitax/leetcode
Log | Files | Refs | sfeed.txt

0773-sliding-puzzle.rs (4746B)


      1/// 773. Sliding Puzzle (Hard)
      2///
      3/// On an 2 x 3 board, there are five tiles labeled from 1 to 5, and an empty
      4/// square represented by 0. A *move* consists of choosing 0 and a
      5/// 4-directionally adjacent number and swapping it.
      6///
      7/// The state of the board is solved if and only if the board is
      8/// [[1,2,3],[4,5,0]].
      9///
     10/// Given the puzzle board board, return the least number of moves required so
     11/// that the state of the board is solved. If it is impossible for the state of
     12/// the board to be solved, return -1.
     13///
     14/// *Example 1:*
     15///
     16///   *Input:* board = [[1,2,3],[4,0,5]]
     17///   *Output:* 1
     18///   *Explanation:* Swap the 0 and the 5 in one move.
     19///
     20/// *Example 2:*
     21///
     22///   *Input:* board = [[1,2,3],[5,4,0]]
     23///   *Output:* -1
     24///   *Explanation:* No number of moves will make the board solved.
     25///
     26/// *Example 3:*
     27///
     28///   *Input:* board = [[4,1,2],[5,0,3]]
     29///   *Output:* 5
     30///   *Explanation:* 5 is the smallest number of moves that solves the board.
     31///   An example path:
     32///   After move 0: [[4,1,2],[5,0,3]]
     33///   After move 1: [[4,1,2],[0,5,3]]
     34///   After move 2: [[0,1,2],[4,5,3]]
     35///   After move 3: [[1,0,2],[4,5,3]]
     36///   After move 4: [[1,2,0],[4,5,3]]
     37///   After move 5: [[1,2,3],[4,5,0]]
     38///
     39/// *Constraints:*
     40///
     41///   * board.length == 2
     42///   * board[i].length == 3
     43///   * 0 <= board[i][j] <= 5
     44///   * Each value board[i][j] is *unique*.
     45///
     46use leetcode::*;
     47use std::cmp::Ordering;
     48use std::collections::hash_map;
     49use std::collections::{BinaryHeap, HashMap};
     50
     51type Board = [u8; 6];
     52
     53const FINAL: Board = [1, 2, 3, 4, 5, 0];
     54
     55#[derive(Clone, Eq)]
     56struct State {
     57    board: Board,
     58    pos: u8,
     59    dist: u32,
     60    cost: u32,
     61}
     62
     63impl State {
     64    fn new(board: Board, pos: u8) -> Self {
     65        Self {
     66            board,
     67            pos,
     68            dist: 0,
     69            cost: Self::board_cost(board),
     70        }
     71    }
     72
     73    fn swap(&self, pos: u8) -> Self {
     74        let mut board = self.board;
     75        board.swap(self.pos as usize, pos as usize);
     76        Self {
     77            board,
     78            dist: self.dist + 1,
     79            cost: self.dist + 1 + Self::board_cost(board),
     80            pos,
     81        }
     82    }
     83
     84    fn board_cost(board: Board) -> u32 {
     85        (0..6)
     86            .filter(|i| board[*i] > 0)
     87            .map(|i| (i as u32).abs_diff(board[i] as u32 - 1))
     88            .sum()
     89    }
     90}
     91
     92impl PartialEq for State {
     93    fn eq(&self, other: &Self) -> bool {
     94        self.cost == other.cost
     95    }
     96}
     97
     98impl Ord for State {
     99    fn cmp(&self, other: &Self) -> Ordering {
    100        other.cost.cmp(&self.cost)
    101    }
    102}
    103
    104impl PartialOrd for State {
    105    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
    106        Some(self.cmp(other))
    107    }
    108}
    109
    110struct Solution {}
    111
    112impl Solution {
    113    fn push_swap(
    114        queue: &mut BinaryHeap<State>,
    115        distmap: &mut HashMap<Board, u32>,
    116        state: &State,
    117        pos: u8,
    118    ) {
    119        let nstate = state.swap(pos);
    120        match distmap.entry(nstate.board) {
    121            hash_map::Entry::Occupied(mut entry) => {
    122                let prev_dist = entry.get_mut();
    123                if nstate.dist < *prev_dist {
    124                    *prev_dist = nstate.dist;
    125                    queue.push(nstate);
    126                }
    127            }
    128            hash_map::Entry::Vacant(entry) => {
    129                entry.insert(nstate.dist);
    130                queue.push(nstate);
    131            }
    132        };
    133    }
    134
    135    pub fn sliding_puzzle(board: Vec<Vec<i32>>) -> i32 {
    136        let board: Board = (0..6)
    137            .map(|i| board[i / 3][i % 3] as u8)
    138            .collect::<Vec<_>>()
    139            .try_into()
    140            .unwrap();
    141        let pos = board.iter().enumerate().find(|(_, v)| **v == 0).unwrap().0 as u8;
    142        let mut queue = BinaryHeap::new();
    143        let mut distmap = HashMap::new();
    144        queue.push(State::new(board, pos));
    145        while let Some(state) = queue.pop() {
    146            if state.board == FINAL {
    147                return state.dist as i32;
    148            }
    149            Self::push_swap(&mut queue, &mut distmap, &state, (state.pos + 3) % 6);
    150            if (state.pos % 3) > 0 {
    151                Self::push_swap(&mut queue, &mut distmap, &state, state.pos - 1);
    152            }
    153            if (state.pos % 3) < 2 {
    154                Self::push_swap(&mut queue, &mut distmap, &state, state.pos + 1);
    155            }
    156        }
    157        -1
    158    }
    159}
    160
    161pub fn main() {
    162    println!("{}", Solution::sliding_puzzle(arg_into(1)));
    163}
    164
    165#[cfg(test)]
    166mod tests {
    167    use crate::Solution;
    168    use leetcode::vvi;
    169
    170    #[test]
    171    fn test() {
    172        assert_eq!(Solution::sliding_puzzle(vvi("[[1,2,3],[4,0,5]]")), 1);
    173        assert_eq!(Solution::sliding_puzzle(vvi("[[1,2,3],[5,4,0]]")), -1);
    174        assert_eq!(Solution::sliding_puzzle(vvi("[[4,1,2],[5,0,3]]")), 5);
    175    }
    176}