8数码问题

八数码游戏(八数码问题)描述为:在3×3组成的九宫格棋盘上,摆有八个将牌,每一个将牌都刻有1-8八个数码中的某一个数码。棋盘中留有一个空格,允许其周围的某一个将牌向空格移动,这样通过移动将牌就可以不断改变将牌的布局。这种游戏求解的问题是:给定一种初始的将牌布局或结构(称初始状态)和一个目标的布局(称目标状态),问如何移动将牌,实现从初始状态到目标状态的转变。

对于八数码问题的解决,首先要考虑是否有答案。每一个状态可认为是一个1×9的矩阵,问题即通过矩阵的变换,是否可以变换为目标状态对应的矩阵?由数学知识可知,可计算这两个有序数列的逆序值,如果两者都是偶数或奇数,则可通过变换到达,否则,这两个状态不可达。这样,就可以在具体解决问题之前判断出问题是否可解,从而可以避免不必要的搜索。

A*:启发中的估价是用估价函数表示的,如:f(n) = g(n) + h(n)  其中f(n) 是节点n的估价函数,g(n)是在状态空间中从初始节点到n节点的实际代价,h(n)是从n到目标节点最佳路径的估计代价。
在此八数码问题中,显然g(n)就是从初始状态变换到当前状态所移动的步数,估计代价h(n)我们就可采用当前状态各个数字牌不在目标状态未知的个数,即错位数。

程序设计步骤:

有open、close、distance三个列表,其中open放的是上一次迭代过程中产生的周围点(周围点不能在close表中,因为不能走已经走过的路),distance放的是所有已经走过的点的最好f值,close放的是已经走过的点

每一次通过得到open中的第一个值temp(open已经排序好的)并将open清空,从而得到temp的周围点(排除已经在close中的点,并且由distance进行更新,即如果比distance中该点的值大,则用distance中该点进行更新,否则将distance进行更新),并将周围点放入open中。进入下一次迭代过程

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class EightPuzzleAlgorithm {
    //八数码问题

    public void Search(EightNode start,EightNode target,List<EightNode> open,List<EightNode> close,List<EightNode> distance){//distance值储存open中状态的f值

//        if(!IsSolution(start.getNodeValue(),target.getNodeValue())){
//            System.out.println("两个数不满足条件,不能查找");
//            return;
//        }

        if(close==null){
            close = new ArrayList();
        }

        if(open.size()==0){
            System.out.println("查找失败!!!");
            return;
        }
        if(open.get(0).equals((target))){
            System.out.println("查找成功!!!");
            return;
        }
        //对于已经排序好的open来说,选出f值最小的状态
        System.out.println("************************");
        EightNode temp = open.get(0);
        int[]tempNode = temp.getNodeValue();
        for(int i = 0;i<3;i++){
            for(int j = 0;j<3;j++){
                System.out.print("  "+tempNode[i*3+j]);
            }
            System.out.println();
        }

        List<int[]>arround = move(temp);
        System.out.println(arround.size()+"********&&***&&");

        //产生不包括在close中的arroundNode列表
        List<EightNode>arroundNode = new ArrayList<EightNode>();
        for(int i = 0;i<arround.size();i++){
            EightNode node = new EightNode();
            node.setF(fValue(temp,arround.get(i),target));
            node.setG(gValue(temp));
            node.setH(hValue(temp,arround.get(i),target));
            node.setNodeValue(arround.get(i));
            if (!close.contains(node)){
                arroundNode.add(node);
            }
        }
        close.add(temp);

        //更新distance(distance用于储存所有走过点的周围点的信息)。
        for(int i = 0;i<arroundNode.size();i++){
            boolean flag = false;
            for(int j = 0;j<distance.size();j++){
                if(arroundNode.get(i).equals(distance.get(j))){
                    flag = true;
                    if(distance.get(j).getF()>arroundNode.get(i).getF()){
                        distance.set(j, arroundNode.get(i));
                    }
                    break;
                }
            }
            if(flag == false){
                distance.add(arroundNode.get(i));
            }
        }

        //更新open
        open = new ArrayList();
        open.addAll(arroundNode);
        System.out.println(open.size()+"***********&&");
        for(int i = 0;i<open.size();i++){
            for(int j = 0;j<distance.size();j++){
                if(open.get(i).equals(distance.get(j))){
                    open.set(i, distance.get(j));
                }
            }
        }
        System.out.println(open.size()+"***…………********&&");
        open = sort(open);

        Search(start,target,open,close,distance);
    }

    public int hValue(EightNode temp1,int[]arroundi,EightNode target1){
        int[]temp = temp1.getNodeValue();
        int[]target=target1.getNodeValue();
        int h = 0;
        for(int i = 0;i<target.length;i++){
            if(target[i]!=arroundi[i]){
                h=h+1;
            }
        }
        return h;
    }

    public List<EightNode>sort(List<EightNode>open){
        for(int i = 0;i<open.size();i++){
            for(int j =i+1;j<open.size();j++){
                if(open.get(i).getF()>open.get(j).getF()){
                    EightNode temp = open.get(i);
                    open.set(i, open.get(j));
                    open.set(j, temp);
                }
            }
        }
        return open;
    }

    public int gValue(EightNode temp){
        return temp.getG()+1;
    }
    public int fValue(EightNode temp1,int[]arroundi,EightNode target1){
        return hValue(temp1,arroundi,target1)+gValue(temp1);
    }

    //对于一个open选出来的状态,如何获取它邻近的状态
    public List<int[]> move(EightNode temp1){
        int[]temp = temp1.getNodeValue();
        List<int[]>list = new ArrayList<int[]>();
        //先找出0(空格所在的位置)
        int position = 0;
        for(int i = 0;i<temp.length;i++){
            if(temp[i]==0){
                position = i;
                break;
            }
        }
        if(position==0){
            list.add(swap(temp.clone(),0,1));
            list.add(swap(temp.clone(),0,3));
        }else if(position==1){
            list.add(swap(temp.clone(),1,0));
            list.add(swap(temp.clone(),1,4));
            list.add(swap(temp.clone(),1,2));
        }else if(position==2){
            list.add(swap(temp.clone(),2,1));
            list.add(swap(temp.clone(),5,2));
        }else if(position==3){
            list.add(swap(temp.clone(),3,0));
            list.add(swap(temp.clone(),3,4));
            list.add(swap(temp.clone(),3,6));
        }else if(position==4){
            list.add(swap(temp.clone(),4,1));
            list.add(swap(temp.clone(),4,3));
            list.add(swap(temp.clone(),4,5));
            list.add(swap(temp.clone(),4,7));
        }else if(position==5){
            list.add(swap(temp.clone(),5,2));
            list.add(swap(temp.clone(),5,8));
            list.add(swap(temp.clone(),5,4));
        }else if(position==6){
            list.add(swap(temp.clone(),6,3));
            list.add(swap(temp.clone(),6,7));
        }else if(position==7){
            list.add(swap(temp.clone(),7,6));
            list.add(swap(temp.clone(),7,8));
            list.add(swap(temp.clone(),7,4));
        }else if(position==8){
            list.add(swap(temp.clone(),8,5));
            list.add(swap(temp.clone(),8,7));
        }
        return list;
    }

    public int[] swap(int[] temp,int i,int j){
        int a = temp[i];
        temp[i] = temp[j];
        temp[j] = a;
        return temp;
    }

    //判断是否有解,两个数组的逆序数同为偶数或是奇数
    public  boolean IsSolution(int []current,int[]target){
        int n1 = InverseNumber(current);
        int n2 = InverseNumber(target);
        if(n1%2==n2%2){
            return true;
        }else{
            return false;
        }
    }

    private  int InverseNumber(int[] current) {
        int length1 = current.length;
        int count1 = 0;
        for(int i = 0;i<length1;i++){
            int temp = current[i];
            for(int j = i+1;j<length1;j++){
                if(temp>current[j]){
                    count1 = count1+1;
                }
            }
        }
        return count1;
    }

}
时间: 2024-11-08 12:51:40

8数码问题的相关文章

1225 八数码难题

1225 八数码难题 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 钻石 Diamond 题解 查看运行结果 题目描述 Description Yours和zero在研究A*启发式算法.拿到一道经典的A*问题,但是他们不会做,请你帮他们.问题描述 在3×3的棋盘上,摆有八个棋子,每个棋子上标有1至8的某一数字.棋盘中留有一个空格,空格用0来表示.空格周围的棋子可以移到空格中.要求解的问题是:给出一种初始布局(初始状态)和目标布局(为了使题目简单,设目标状态为123804765

入侵某有限公司#并成功提下西部数码某虚拟主机

目标站点存在dede5.7通用的sql注入漏洞 利用工具  直接插入一个管理员 随后通过某大牛的爬虫工具获取到后台地址为 http://www.syguizaoni.com/gzhhoutai/index.php 登录 直接在功能表里的文件式管理器里面上传一个大马 大马地址: http://www.syguizaoni.com/uploads/php.php 密码:520 可以跨目录查看多个站群  便知道这个服务器是一个虚拟主机 然后上传一个小马  准备提权 http://www.syguiza

HDU 1043 Eight八数码解题思路(bfs+hash 打表 IDA* 等)

题目链接 https://vjudge.net/problem/HDU-1043 经典的八数码问题,学过算法的老哥都会拿它练搜索 题意: 给出每行一组的数据,每组数据代表3*3的八数码表,要求程序复原为初始状态 思路: 参加网站比赛时拿到此题目,因为之前写过八数码问题,心中暗喜,于是写出一套暴力bfs+hash,结果TLE呵呵 思路一:bfs+hash(TLE) 1 #include <cstdio> 2 #include <cstring> 3 #include <queu

洛谷【P1379】八数码难题

P1379 八数码难题 题目描述 在3×3的棋盘上,摆有八个棋子,每个棋子上标有1至8的某一数字.棋盘中留有一个空格,空格用0来表示.空格周围的棋子可以移到空格中.要求解的问题是:给出一种初始布局(初始状态)和目标布局(为了使题目简单,设目标状态为123804765),找到一种最少步骤的移动方法,实现从初始布局到目标布局的转变. 输入输出格式 输入格式: 输入初试状态,一行九个数字,空格用0表示 输出格式: 只有一行,该行只有一个数字,表示从初始状态到目标状态需要的最少移动次数(测试数据中无特殊

八数码三种用时差距极大的写法

进化史,一种比一种长,一种比一种快.不过第三种似乎还不是最终形态. 第一种,傻逼级迭代加深. 去年十一月写的,那时候刚刚学迭代加深,敲了一个钟头才敲完,codevs上直接过,就没太管,觉得这是个水题.实际上呢,看后文. 1 #include<algorithm> 2 #include<iostream> 3 #include<cstring> 4 #include<cstdio> 5 using namespace std; 6 int sx,sy,lim,

八数码的八境界 [转载]

八数码的八境界   研究经典问题,空说不好,我们拿出一个实际的题目来演绎.八数码问题在北大在线测评系统中有一个对应的题,题目描述如下: Eight Time Limit: 1000MS    Memory Limit: 65536K  Special Judge Description The 15-puzzle has been aroundfor over 100 years; even if you don't know it by that name, you've seen it. I

树莓派控制数码管显示

1. 效果显示 2. 硬件设备 在某宝上买了一个4位共阴数码管.4个数字公用一个阴极,一共12个引脚.其中6,8,9,12是共阴引脚. 每个数字由 a-g 7个二极管和一个点dp.因为是在网上买的没有详细的器件手册,所以只能自己测试引脚与数码管上二极管的对应关系. 通过上图可以看出12号引脚对应的是第一位数字的共阴引脚,1号引脚对应数字e.依次测试,结果如下: 2.

(五)数码管仿真 02

电路图:注意 为了省事 把 数码管串联的电阻省去了 参考代码:  这里 检测按键并不好,抄的另外一篇日志,具体还要看书 #include<reg52.h> #include<intrins.h> #define uchar8 unsigned char #define uint16 unsigned int sbit key1=P2^0; sbit key2=P2^1; sbit key3=P2^2; sbit key4=P2^3; uchar8 code seg[]={ 0x3F

我的 FPGA 学习历程(10)&mdash;&mdash; 实验数码管驱动

根据黑金 AX301 手册,数码管位选信号命名为 SEL[5:0],其中 SEL[5] 对应最左边的数码管,而SEL[0] 对应最右边数码管:作为约定,在下面的描述中我们对应的称之为数码管 5 和数码管 0.数码管的段选信号被命名为 DIG[7:0]:DIG[7] 为小数点 DP,DIG[6] 为数码管 g 段,DIG[0] 为 a 段,其他类推即可. 由于数码管的段选信号是共用的,理论上是不可能同时显示六个不同字符的,但由于人眼的视觉残留现象,只要我们很快的依次显示每一个数字,就可以欺骗人眼达

单片机4*4按键数码管显示0-9

#include<reg51.h> #define KEY P1 //键盘 #define SEG P0 //数码管 sbit COM=P2^0; // 数码管com sbit OC = P2^1;//573锁存器 char code TAB[]={//0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0x88,0x83,0xa7,0xa1,0x84,0x84,0xbf,0x7f};//共阳极 0x3f,0x06,0x5b,0x4f,0x66,