Java DFS实现六数码问题

Java DFS实现六数码问题

问题描述

现有一两行三列的表格如下:

A B C

D E F

把1、2、3、4、5、6六个数字分别填入A、B、C、D、E、F格子中,每个格子一个数字且各不相同。每种不同的填法称为一种布局。如下:

1 3 5

2 4 6

布局1

2 5 6

4 3 1

布局2

定义α变换如下:把A格中的数字放入B格,把B格中的数字放入E格,把E格中的数字放入D格,把D格中的数字放入A格。

定义β变换如下:把B格中的数字放入C格,把C格中的数字放入F格,把F格中的数字放入E格,把E格中的数字放入B格。

问:对于给定的布局,可否通过有限次的α变换和β变换变成下面的目标布局:

1 2 3

4 5 6

输入:本题有多个测例,第一行为输入测例的个数n,下面是n行测例,每个测例的输入是1到6这六个数字的一个排列,空格隔开,表示初始布局ABCDEF格中依次填入的数字。

输出:每个输出占一行。输出转换到目标格局需要变换的最少次数。(若不能转换则输出-1)

输入样例:

2

2 5 3 1 4 6

2 3 6 1 5 4

输出样例:

1

2

注意不能转换到目标格局的情况应输出-1;

输出格式为:printf(“%d\n”,min);

问题理解

通过输入的6位数字,将数字转换为2*3的二维数组,将数组通过有限次数的α变换和β变换得到目标数组。根据排列组合,输入的情况总共有720种(6 * 5 * 4 * 3 * 2 * 1)。本题可以采用回溯法解决。

问题解的形式表示为(change,len,flag)组成,change表示变换的方式序列,分别用a、b表示,len表示最少变换的次数,flag表示是否是问题的解。

显式约束:通过9次变换得到目标数组。

隐式约束:变换过程中不能变换出之前变换出的数组。

解空间:根据显示约束,每一次变换有两种变换方式,最多可以变换9次,所以解空间大小为2^9种。

代码

node.java

public class Node {
    /**
     * value 记录当前节点的值,
     * change 记录到当前节点经过了哪些变换,变换方式只有a或b。
     * len 记录当前节点变换了多少次
     * flag 记录当前节点是否是一个解
     */
    public int[] value;
    public String change;
    public int len;
    public boolean flag = false;

    public int[] getValue() {
        return value;
    }

    public void setValue(int[] value) {
        this.value = value;
    }

    public String getChange() {
        return change;
    }

    public void setChange(String change) {
        this.change = change;
    }

    public int getLen() {
        return len;
    }

    public void setLen(int len) {
        this.len = len;
    }

    public Node(int[] value, String change, int len) {
        this.value = value;
        this.change = change;
        this.len = len;
    }
    /**
     * a变换
     * @return 返回变换后的数组
     */
    public int[] changeA(){
        int[] ans = this.value.clone();
        int tem = ans[0];
        ans[0] = ans[3];
        ans[3] = ans[4];
        ans[4] = ans[1];
        ans[1] = tem;
        return ans;
    }

    /**
     * b变换
     * @return 返回变换后的数组
     */
    public int[] changeB(){
        int[] ans = this.value.clone();
        int tem = ans[1];
        ans[1] = ans[4];
        ans[4] = ans[5];
        ans[5] = ans[2];
        ans[2] = tem;
        return ans;
    }

    /**
     * 将int数组得到String
     * @return String
     */
    public String getString(){
        String tem = "";
        int[] ans = this.value.clone();
        for (int i=0;i<ans.length;i++){
            tem = tem + ans[i];
        }
        return tem;
    }
}

Tree.java

public class Tree {
    public Node node;
    public Tree left = null;
    public Tree right = null;

    public Node getNode() {
        return node;
    }

    public void setNode(Node node) {
        this.node = node;
    }

    public Tree getLeft() {
        return left;
    }

    public void setLeft(Tree left) {
        this.left = left;
    }

    public Tree getRight() {
        return right;
    }

    public void setRight(Tree right) {
        this.right = right;
    }

    public Tree(Node node) {
        this.node = node;
    }

    public Tree(int[] ans,String change ,int len){
        this.node = new Node(ans,change,len);
    }
}

do.java

public class Do {
    public static Set<String> set = new TreeSet<>();
    public static Set<String> set1 = new TreeSet<>();

    final static String last = "123456";
//    public static void main(String[] args) {
//        //实习要求的主程序
//        Main();
//        //判断有多少种有解情况
        countAll();
//        //计算所有输入情况,并判断解是否正确
        countSolve();
//
//    }

    //实习要求的主程序
    public static void Main(){
        Scanner input = new Scanner(System.in);
        int n = input.nextInt();
        Tree root ;
        int[] tem;
        for(int i = 0; i < n; i++){
            tem = inputArray(6);
//            String origin = ""+tem[0]+tem[1]+tem[2]+tem[3]+tem[4]+tem[5];
//            tem = getArrByString("123456");
            root = new Tree(tem,"",0);
//            DFS1(root,origin);
            root = new Tree(tem,"",0);
            DFS(root);
            System.out.println(searchResult(root).len);
        }
    }

    /**
     *  生成结果树
     * @param root 根节点
     */
    public static void DFS(Tree root){
        String value = root.node.getString();
        if (set.contains(value) || set.size()>9){
            return ;
        }

        if (value.equals(last)){
            root.node.flag = true;
            return ;
        }
        set.add(value);
        root.left = new Tree(root.node.changeA(),root.node.change+"a",root.node.len+1);
        DFS(root.left);
        root.right = new Tree(root.node.changeB(),root.node.change+"b",root.node.len+1);
        DFS(root.right);
        set.remove(value);
    }

    public static void DFS1(Tree root,String origin){
        String value = root.node.getString();
        if (set.contains(value)){
            return ;
        }

        if (value.equals(origin)){
            root.node.flag = true;
            return ;
        }
        set.add(value);
        root.left = new Tree(root.node.changeA(),root.node.change+"a",root.node.len+1);
        DFS1(root.left,origin);
        root.right = new Tree(root.node.changeB(),root.node.change+"b",root.node.len+1);
        DFS1(root.right,origin);
        set.remove(value);
    }


    /**
     * 查询结果集
     * @param root 根节点
     * @return 最小的结果节点
     */
    public static Node searchResult(Tree root){
        if (root.node.flag){
            return root.node;
        }
        Queue<Tree> queue = new LinkedList<Tree>();
        queue.offer(root);
        Node min = new Node(null,"",Integer.MAX_VALUE);
        Tree tem ;
        while (!queue.isEmpty()){
            tem = queue.poll();
            if (tem.left!=null){
                queue.offer(tem.left);
            }
            if (tem.right!=null){
                queue.offer(tem.right);
            }
            if (tem.node.flag==true && tem.node.len < min.len){
                min = tem.node;
            }
        }
        if (min.len==Integer.MAX_VALUE){
            min.len=-1;
        }
        return min;
    }

    /**
     * 构建数组
     * @param len 输入数组的长度
     * @return 返回输入的数组
     */
    public static int[] inputArray(int len){
        int[] ans = new int[len];
        Scanner in = new Scanner(System.in);
        for (int i=0; i<len;i++){
            ans[i] = in.nextInt();
        }
        return ans;
    }

    // 初始化输入 将生成的所有输入保存到set1集合中。
    public static void DFS_Input_init(String s){
        if (s.length()==6){
            set1.add(s);
        }
        for(int i=1;i<7;i++){
            if (s.contains(""+i)){
                continue;
            }else {
                DFS_Input_init(s+i);
            }
        }
    }

    // 将字符串转换为二维数组
    public static int[] getArrByString(String s){
        int ans[] = new int[s.length()];
        for (int i=0;i<s.length();i++){
            ans[i] = Integer.parseInt(s.substring(i,i+1));
        }
        return ans;
    }

    //判断当前节点是否能按照结果变换到目标字符串
    public static boolean check(Node node,String context){
        boolean t = false;
        int[] tem;
        for (int i=0;i<node.change.length();i++){
            if (node.change.charAt(i) =='a'){
                tem = node.changeA();
                node.value = tem.clone();
            }else {
                tem = node.changeB();
                node.value = tem.clone();
            }
        }
        if (node.getString().equals(context)){
            t = true;
        }
        return t;
    }

    /**
     * 生成游街情况的树,set集合中元素的个数即为有解情况的种数
     * @param root 生成树的根节点
     */
    public static void count(Tree root){
        String value = root.getNode().getString();
        if (set.contains(value)){
            return ;
        }
        set.add(value);
        int[] temp = root.getNode().changeA();
        root.node.value = temp.clone();
        temp = root.getNode().changeA();
        root.node.value = temp.clone();
        temp = root.getNode().changeA();
        root.node.value = temp.clone();
        root.left = new Tree(temp,"a"+root.node.change,root.node.len+1);
        count(root.left);
        temp = root.getNode().changeA();
        root.node.value = temp.clone();

        temp = root.getNode().changeB();
        root.node.value = temp.clone();
        temp = root.getNode().changeB();
        root.node.value = temp.clone();
        temp = root.getNode().changeB();
        root.node.value = temp.clone();
        root.right = new Tree(temp,"b"+root.node.change,root.node.len+1);
        count(root.right);
    }

    //计算有解情况的种数主程序
    public static void countAll(){
        set.clear();
        Tree root = new Tree(new int[]{1,2,3,4,5,6},"",0);
        count(root);
        System.out.println(set.size());
    }

    //计算所有输入情况,并判断解是否正确
    public static void countSolve(){
        Tree root;
        DFS_Input_init("");
        Iterator<String> iterator = set1.iterator();
        while (iterator.hasNext()){
            set.clear();
            String temp = iterator.next();
            int[] arr = getArrByString(temp);
            root = new Tree(arr,"",0);
            DFS(root);
            Node node = searchResult(root);
            boolean t = true;
            if (node.len!=-1){
                node.value = (getArrByString(temp)).clone();
                t = check(node,"123456");
            }
            System.out.println(temp+"\t"+node.len+"\t"+node.change+"\t"+t);
        }
    }

}

增加web页面

index.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
<head>
    <meta charset="UTF-8">
    <title>六数码问题求解</title>
    <link rel="stylesheet" href="/css/style.css">
</head>
<body>
    <div id="main">
        <div id="titel">
            <h2>六数码问题求解</h2>
        
        <label for="main-display">演示框</label>
        <table id="main-display" border="1px">
            <tr>
                <td class="tableimg" id="img0" value=""></td>
                <td class="tableimg" id="img1" value=""></td>
                <td class="tableimg" id="img2" value=""></td>
            </tr>
            <tr>
                <td class="tableimg" id="img3" value=""></td>
                <td class="tableimg" id="img4" value=""></td>
                <td class="tableimg" id="img5" value=""></td>
            </tr>
        </table>
        </div>
        <label for="main-input">点击图片输入到演示框</label>
        <table id="main-input" border="1px" >
            <tr>
                <td class="tableimg" >
                    <img id="imginput0" class="input" src="/img/blue_1.png" alt="无图片" value="1">
                    <!-- <img id="imginput0" class="input" src="./../static/img/blue_1.png" alt="无图片" value="1"> -->
                </td>
                <td class="tableimg" >
                    <img id="imginput1" class="input" src="/img/blue_2.png" alt="无图片" value="2">
                    <!-- <img id="imginput1" class="input" src="./../static/img/blue_2.png" alt="无图片" value="2"> -->
                </td>
                <td class="tableimg" >
                    <img id="imginput2" class="input" src="/img/blue_3.png" alt="无图片" value="3">
                    <!-- <img id="imginput2" class="input" src="./../static/img/blue_3.png" alt="无图片" value="3"> -->
                </td>
            </tr>
            <tr>
                <td class="tableimg" >
                    <img id="imginput3" class="input" src="/img/blue_4.png" alt="无图片" value="4">
                    <!-- <img id="imginput3" class="input" src="./../static/img/blue_4.png" alt="无图片" value="4"> -->
                </td>
                <td class="tableimg" >
                    <img id="imginput4" class="input" src="/img/blue_5.png" alt="无图片" value="5">
                    <!-- <img id="imginput4" class="input" src="./../static/img/blue_5.png" alt="无图片" value="5"> -->
                </td>
                <td class="tableimg" >
                    <img id="imginput5" class="input" src="/img/blue_6.png" alt="无图片" value="6">
                    <!-- <img id="imginput5" class="input" src="./../static/img/blue_6.png" alt="无图片" value="6"> -->
                </td>
            </tr>
        </table>
        <button id="rewrite">复位</button>
        <button id="solve">运算</button>
        <button id="display">演示</button>
        <!-- <img src="./../static/img/blue_1.png" id="m12"> -->
        <!-- <label for="ans">最少运算次数</label> -->
        <!-- <cite th:text="${session.ans}" id="ans"></cite> -->
    </div>

</body>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.js"></script>
<script>
    $(document).ready(function(){
        var len = 0;
        var ans = ""
        var index = 0
        
        $(".input").click(function(){
            $(this).hide();
            var temid = "img"+len;
            var imgsrc = this.src;
            // console.log(imgsrc)
            var value = this.id;
            value =value.replace("imginput","");
            value++;
            // console.log(value)
            document.getElementById(temid).innerHTML="<img id='img1"+len+"' src="+imgsrc+">"
            temid="#"+temid
            $(temid).val(value)
            console.log($(temid).val())
            len++;
        })

        $("#rewrite").click(function(){
            len=0
            $(".input").show();
            document.getElementById("img0").innerHTML=""
            document.getElementById("img1").innerHTML=""
            document.getElementById("img2").innerHTML=""
            document.getElementById("img3").innerHTML=""
            document.getElementById("img4").innerHTML=""
            document.getElementById("img5").innerHTML=""

        })

        $("#solve").click(function(){
            if(len!=6){
                alert("请先填完数字!")
                return;
            }
            var a0 = $("#img0").val()
            var a1 = $("#img1").val()
            var a2 = $("#img2").val()
            var a3 = $("#img3").val()
            var a4 = $("#img4").val()
            var a5 = $("#img5").val()
            $.ajax({
                type:'get',
                url:'/solve?a0='+a0+'&a1='+a1+'&a2='+a2+'&a3='+a3+'&a4='+a4+'&a5='+a5,
                success(data){
                    alert(data.message+data.status+"结果为"+data.ans+"\n"+data.change)
                    ans = data.change
                    index = 0;
                }

            })
        })
        
        $("#display").click(function(){
            if(ans==""){
                alert("不需要变换")
                return;
            }
            if(ans=="-1"){
                alert("不能变换到目标数组")
                return;
            }
            if(index>=ans.length){
                alert("变换完成")
                return;
            }
            if(ans.charAt(index)=='a'){
                changeA()
            }else{
                changeB()
            }
            index++
            console("index:"+index)
        })

        function changeA(){
            $('#img10').animate({
            left:'57px',
            }  
            ,1000)
            $("#img11").animate({
             top:'57px',   
            }
            ,1000)
            $("#img14").animate({
             left:'-57px',   
            }
            ,1000)
            $("#img13").animate({
             top:'-57px',   
            }
            ,1000)
            setTimeout(function(){
            var temsrc0 = document.getElementById("img10").getAttribute("src");
            var temsrc1 = document.getElementById("img11").getAttribute("src");
            var temsrc3 = document.getElementById("img13").getAttribute("src");
            var temsrc4 = document.getElementById("img14").getAttribute("src");
            document.getElementById("img10").setAttribute("src",temsrc3);
            document.getElementById("img13").setAttribute("src",temsrc4);
            document.getElementById("img14").setAttribute("src",temsrc1);
            document.getElementById("img11").setAttribute("src",temsrc0);
            
            document.getElementById("img10").removeAttribute("style");
            document.getElementById("img13").removeAttribute("style");
            document.getElementById("img14").removeAttribute("style");
            document.getElementById("img11").removeAttribute("style");


            // document.getElementById("img10").setAttribute("style","");
            // document.getElementById("img13").setAttribute("style","");
            // document.getElementById("img14").setAttribute("style","");
            // document.getElementById("img11").setAttribute("style","");
            },1500)
        }
        
        function changeB(){
            $('#img11').animate({
            left:'57px',
            }  
            ,1000)
            $("#img12").animate({
             top:'57px',   
            }
            ,1000)
            $("#img15").animate({
             left:'-57px',   
            }
            ,1000)
            $("#img14").animate({
             top:'-57px',   
            }
            ,1000)
            setTimeout(function(){
            var temsrc1 = document.getElementById("img11").getAttribute("src");
            var temsrc2 = document.getElementById("img12").getAttribute("src");
            var temsrc4 = document.getElementById("img14").getAttribute("src");
            var temsrc5 = document.getElementById("img15").getAttribute("src");
            document.getElementById("img11").setAttribute("src",temsrc4);
            document.getElementById("img14").setAttribute("src",temsrc5);
            document.getElementById("img15").setAttribute("src",temsrc2);
            document.getElementById("img12").setAttribute("src",temsrc1);

            document.getElementById("img11").removeAttribute("style");
            document.getElementById("img14").removeAttribute("style");
            document.getElementById("img15").removeAttribute("style");
            document.getElementById("img12").removeAttribute("style");
            // document.getElementById("img11").setAttribute("style","");
            // document.getElementById("img14").setAttribute("style","");
            // document.getElementById("img15").setAttribute("style","");
            // document.getElementById("img12").setAttribute("style","");
            },1500)
        }

    })
</script>
</html>

带Web页面效果截图

在这里插入图片描述

演示框可将运算后的结果进行演示(演示过程中别点复位!!!),点一次变换一次。

带页面的项目文件下载[项目已打包成jar 可直接运行]

下载文件后,
打开cmd
进入到包含jar文件的目录下
输入:
java -jar [文件名].jar

效果展示:

刚开始启动时的画面:

在这里插入图片描述

启动成功的画面:
在这里插入图片描述

在浏览器中输入127.0.0.1:8080 即可

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值