773-Sliding Puzzle

Description:
On a 2x3 board, there are 5 tiles represented by the integers 1 through 5, and an empty square represented by 0.

A move consists of choosing 0 and a 4-directionally adjacent number and swapping it.

The state of the board is solved if and only if the board is [[1,2,3],[4,5,0]].

Given a puzzle board, return the least number of moves required so that the state of the board is solved. If it is impossible for the state of the board to be solved, return -1.

Examples:

Input: board = [[1,2,3],[4,0,5]]
Output: 1
Explanation: Swap the 0 and the 5 in one move.
Input: board = [[1,2,3],[5,4,0]]
Output: -1
Explanation: No number of moves will make the board solved.
Input: board = [[4,1,2],[5,0,3]]
Output: 5
Explanation: 5 is the smallest number of moves that solves the board.
An example path:
After move 0: [[4,1,2],[5,0,3]]
After move 1: [[4,1,2],[0,5,3]]
After move 2: [[0,1,2],[4,5,3]]
After move 3: [[1,0,2],[4,5,3]]
After move 4: [[1,2,0],[4,5,3]]
After move 5: [[1,2,3],[4,5,0]]
Input: board = [[3,2,4],[1,5,0]]
Output: 14

Note:

board will be a 2 x 3 array as described above.
board[i][j] will be a permutation of [0, 1, 2, 3, 4, 5].

解题思路:
使用BFS。问题的关键是高效的做BFS。

解法1,先看一个反例(我做的。。。):

class Solution {
    private static final int M = 2;
    private static final int N = 3;
    public int slidingPuzzle(int[][] board) {
        Set<Integer> set = new HashSet<>();
        Queue<int[][]> queue = new LinkedList<>();
        int[] dest = new int[]{1, 2, 3, 4, 5, 0};

        queue.offer(board);
        set.add(transform(board));
        int level = 0;
        while(!queue.isEmpty()){
            int size = queue.size();
            while(size > 0){
                int[][] node = queue.poll();
                if(valid(node, dest))   return level;
                int x = 0, y = 0;
                for(int i = 0;i < M;i++){
                    for(int j = 0;j < N;j++){
                        if(node[i][j] == 0){
                            x = i;
                            y = j;
                            break;
                        }
                    }
                }

                if(x - 1 >= 0){
                    int[][] newNode = new int[M][N];
                    for(int i = 0;i < M;i++){
                        for(int j = 0;j < N;j++){
                            newNode[i][j] = node[i][j];
                        }
                    }
                    int temp = newNode[x - 1][y];
                    newNode[x - 1][y] = newNode[x][y];
                    newNode[x][y] = temp;
                    if(set.add(transform(newNode)))    queue.offer(newNode);
                }
                if(x + 1 < M){
                    int[][] newNode = new int[M][N];
                    for(int i = 0;i < M;i++){
                        for(int j = 0;j < N;j++){
                            newNode[i][j] = node[i][j];
                        }
                    }
                    int temp = newNode[x + 1][y];
                    newNode[x + 1][y] = newNode[x][y];
                    newNode[x][y] = temp;
                    if(set.add(transform(newNode)))    queue.offer(newNode);
                }
                if(y - 1 >= 0){
                    int[][] newNode = new int[M][N];
                    for(int i = 0;i < M;i++){
                        for(int j = 0;j < N;j++){
                            newNode[i][j] = node[i][j];
                        }
                    }
                    int temp = newNode[x][y - 1];
                    newNode[x][y - 1] = newNode[x][y];
                    newNode[x][y] = temp;
                    if(set.add(transform(newNode)))    queue.offer(newNode);
                }
                if(y + 1 < N){
                    int[][] newNode = new int[M][N];
                    for(int i = 0;i < M;i++){
                        for(int j = 0;j < N;j++){
                            newNode[i][j] = node[i][j];
                        }
                    }
                    int temp = newNode[x][y + 1];
                    newNode[x][y + 1] = newNode[x][y];
                    newNode[x][y] = temp;
                    if(set.add(transform(newNode)))    queue.offer(newNode);
                }
                size--;
            }
            level++;
        }

        return -1;
    }
    public int transform(int[][] board){
        int sum = 1;

        for(int i = 0;i < M;i++){
            for(int j = 0;j < N;j++){
                sum = sum * 7 + board[i][j];
            }
        }

        return sum;
    }    

    public boolean valid(int[][] board, int[] dest){
        for(int i = 0;i < M;i++){
            for(int j = 0;j < N;j++){
                if(board[i][j] != dest[i * N + j])    return false;
            }
        }

        return true;
    }
}

我的解法犯的关键性错误是数据结构,没有对二维数组进行处理,导致了很多本可以避免的工作。甚至连自己都不清楚为什么只花了28ms(最优解在10ms左右)

解法2(使用了高效的数据结构):

class Solution {
        public int slidingPuzzle(int[][] board) {
        Set<String> seen = new HashSet<>();
        String target = "123450";
        String s = Arrays.deepToString(board).replaceAll("\\[|\\]|,|\\s", "");
        Queue<String> q = new LinkedList<>(Arrays.asList(s));
        int ans = 0;
        int[] d = { 1, -1, 3, -3 };

        seen.add(s); 
        while (!q.isEmpty()) {
            for(int sz = q.size(); sz > 0; --sz){ 
                String str = q.poll();
                if (str.equals(target))     return ans; 
                int i = str.indexOf('0');
                for(int k = 0; k < 4; ++k){
                    int j = i + d[k];
                    if (j < 0 || j > 5 || i == 2 && j == 3 || i == 3 && j == 2)     continue;
                    char[] ch = str.toCharArray();
                    char tmp = ch[i];
                    ch[i] = ch[j];
                    ch[j] = tmp;
                    s = String.valueOf(ch);
                    if(seen.add(s))        q.offer(s);
                }
            }
            ++ans;
        }

        return -1;
    }
}

虽然还有解法比这个解法快一点,但是它应该是最简洁的解法了。关键是将二维的board转化为了字符串,因此以下步骤都很快:
1.查找0的下标。indexOf
2.放入HashSet(我的解法中自己算出的二维数组的hash。。。)
3.形成新的board
4.与目标board的对比。一个equals就解决问题了。。。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值