BFS解决八数码问题-java

本文主要通过BFS广度优先搜索来解决八数码问题。


前言

本文主要通过BFS广度优先搜索来解决八数码问题。


提示:以下是本篇文章正文内容,下面案例可供参考

一、八数码

在一个 3×3的网格中,1∼8 这 8 个数字和一个 x 恰好不重不漏地分布在这 3×3 的网格中。

例如:

1 2 3
x 4 6
7 5 8

我们可以通过x与它的上下左右四个方向之一的数字进行交换作来让最后的网格变成如下正确排列:

1 2 3
4 5 6
7 8 x

 上述过程的具体交换如下:

1 2 3   1 2 3   1 2 3   1 2 3
x 4 6   4 x 6   4 5 6   4 5 6
7 5 8   7 5 8   7 x 8   7 8 x

现在,给你一个初始网格,请你求出得到正确排列至少需要进行多少次交换。

输入格式:

输入占一行,将 3×3的初始网格描绘出来。

 上述输入例如:1 2 3 x 4 6 7 5 8

输出格式:

输出占一行,包含一个整数,表示最少交换次数。

如果不存在解决方案,则输出 −1。

二、算法思路

1.思路模拟

图1.1思路模拟 

如图1.1所示,x有四种交换方式,那我们可以把最后的结果抽象为x在当前位置,走各种情况且最短距离到达最右下角的时候,所得到的最后的序列与正确序列是否相同,相同了输出此时的交换次数即最小交换次数,如果不同,直接打印-1,说明得不到正确的序列。

 x的移动方式我们可以用两个一维数组分别x和y的变化量即dx,dy 

dx = (-1,1,0,0)

 dy = (0,0,-1,1)

 四个变化量分别表示上、下、左、右横纵坐标的变化量。

因为这道题是直接输入了一行,我们可以用字符串start来存储整个序列。

图1.2下标转换 

如图1.2所示矩阵中横坐标x:下标 / 3;矩阵中纵坐标y:下标 % 3

因此当我们用BFS进行时,每一个点的状态我们可以直接用字符串类型的队列queue<String>来存储即可,并且每一个从初始起点到该状态的步数即交换次数用一个键为String、值为Integer的集合dist来存储。   

2.实现思路

我们先向队列中添加起始状态strat,然后向dist中添加起始状态的交换次数0,然后进行循环,当队列非空时,弹出队头元素即此时的状态字符串pool,如果此时poll跟最后状态“12345678x”字符串end相同时说明我们此时已经完成了正确序列的交换并且我们上下左右操作每一次都是在原状态的操作次数加1,此时dist.get(end)就是我们的最小交换次数。当pool跟end不匹配时,那么我们通过图1.2来得到在3*3棋盘中的横纵坐标x、y,然后通过for循环来得到上下左右四个状态对应变化后的横纵坐标a、b。当然我们得判断a、b是否在棋盘中越界,并且我们得上下左右移动完成交换后的新字符串是否在dist集合中出现过,dist.get(str)不为空就说明我们已经走过这个状态,就不必再走了;为空就说明这个状态我们未交换过,那么我们就将新状态压入队列,然后得到原先字符串状态的交换次数即dist.get(pool),然后再把新状态和对应的交换次数(原状态交换次数+1)即dist.map(str,dist.get(pool)+1)放入集合dist。当我们队列为空且最后一个状态仍与结尾状态不一样说明无法得到最后的正确序列,返回-1即可。

三、代码

1.代码如下:


import java.io.*;
import java.util.*;
public class Main {
    static PrintWriter pw = new PrintWriter(new OutputStreamWriter(System.out));
    static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    //初始字符串
    static String start;
    public static void main(String[] args) {
        Scanner sc = new Scanner(br);
        //把输入的一行中的空格全部替换没了
        start = sc.nextLine().replaceAll("\\s+","");
        pw.println(bfs());
        pw.flush();
    }
    public static int bfs(){
        String end = "12345678x";
        Queue<String> queue = new LinkedList<>();
        Map<String,Integer>dist = new HashMap<>();
        queue.add(start);
        dist.put(start,0);
        //上下左右
        int[] dx = {-1,1,0,0};
        int[] dy = {0,0,-1,1};
        while (!queue.isEmpty()){
            //取出队头元素
            String pool = queue.poll();
            if(pool.equals(end)){
                return dist.get(end);
            }
            //找到x的下标
            int k = pool.indexOf('x');
            int x = k / 3;
            int y = k % 3;
            //表示上下左右四个状态
            for(int i = 0;i < 4;i++){
                //变化后的x坐标
                int a = x + dx[i];
                //变化后的y的坐标
                int b = y + dy[i];
                //判断下标是否在矩阵中越界
                if(a >= 0 && a < 3 && b >= 0 && b < 3){
                    char[] arr = pool.toCharArray();
                    //将x和此时变化后的字符进行交换
                    swap(arr,k,a * 3 + b);
                    //将字符数组转换成字符串
                    String str = new String(arr);
                    //说明此时的状态是未走过的
                    if(dist.get(str) == null){
                        //将新状态添加进队列
                        queue.add(str);
                        //走到新状态那就原本的状态对应的字符串交换次数+1
                        dist.put(str, dist.get(pool) + 1);
                    }
                }
            }
        }
        //当我们走完之后字符串与目标序列不同说明根本交换不到正确序列,返回结果-1
        return -1;
    }
    //地址传递 交换字符
    public static void swap(char[] arr,int a,int b){
        char temp = arr[a];
        arr[a] = arr[b];
        arr[b] = temp;
    }
}

2.读入数据

2  3  4  1  5  x  7  6  8 

3.代码运行结果

19

总结

上述也是通过BFS来解决最短路的问题,我们只要分清楚各个状态如何变化和存储,知道一个点是从哪个点抽象变化过来的,就可以解决这一类问题。

  • 30
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
BFS广度优先搜索)是一种常用的图搜索算法,用于解决八数码问题等许多问题八数码问题是一个经典的拼图游戏,目标是将一个3x3的方格中的数字按照特定顺序排列。 在Java中实现BFS八数码问题,可以按照以下步骤进行: 1. 定义一个表示状态的类,可以使用一个3x3的二维数组来表示八数码的当前状态。 2. 创建一个队列(可以使用LinkedList实现),用于存储待搜索的状态。 3. 将初始状态加入队列,并标记为已访问。 4. 进入循环,直到队列为空: - 从队列中取出一个状态。 - 判断是否为目标状态,如果是,则搜索结束。 - 否则,生成当前状态的所有可能下一步状态,并将未访问过的状态加入队列。 5. 如果队列为空仍未找到目标状态,则表示无解。 以下是一个简单的Java代码示例: ```java import java.util.LinkedList; import java.util.Queue; public class BFSEightPuzzle { private static final int[][] DIRECTIONS = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}}; // 上下左右四个方向 public static void main(String[] args) { int[][] startState = {{1, 2, 3}, {4, 5, 6}, {7, 8, 0}}; // 初始状态 int[][] targetState = {{1, 2, 3}, {4, 5, 6}, {7, 8, 0}}; // 目标状态 int steps = bfs(startState, targetState); System.out.println("最少步数:" + steps); } private static int bfs(int[][] startState, int[][] targetState) { int steps = 0; Queue<int[][]> queue = new LinkedList<>(); queue.offer(startState); while (!queue.isEmpty()) { int size = queue.size(); for (int i = 0; i < size; i++) { int[][] currentState = queue.poll(); if (isEqual(currentState, targetState)) { return steps; } int[] zeroPos = findZeroPosition(currentState); int zeroRow = zeroPos[0]; int zeroCol = zeroPos[1]; for (int[] direction : DIRECTIONS) { int newRow = zeroRow + direction[0]; int newCol = zeroCol + direction[1]; if (isValid(newRow, newCol { int[][] nextState = cloneState(currentState); swap(nextState, zeroRow, zeroCol, newRow, newCol); queue.offer(nextState); } } } steps++; } return1; // 无解 } private static boolean isEqual(int[][] state1, int[][] state2) { for (int i = 0; i < state1.length; i++) { for (int j = 0; j < state1[0].length; j++) { if (state1[i][j] != state2[i][j]) { return false; } } } return true; } private static int[] findZeroPosition(int[][] state) { int[] pos = new int[2]; for (int i = 0; i < state.length; i++) { for (int j = 0; j < state[0].length; j++) { if (state[i][j] == 0) { pos[0] = i; pos[1] = j; return pos; } } } return pos; } private static boolean isValid(int row, int col) { return row >= 0 && row < 3 && col >= 0 && col < 3; } private static int[][] cloneState(int[][] state) { int[][] newState = new int[state.length][state[0].length]; for (int i = 0; i < state.length; i++) { for (int j = 0; j < state[0].length; j++) { newState[i][j] = state[i][j]; } } return newState; } private static void swap(int[][] state, int row1, int col1, int row2, int col2) { int temp = state[row1][col1]; state[row1][col1] = state[row2][col2]; state[row2][col2] = temp; } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值