zoukankan      html  css  js  c++  java
  • 广度优先搜索与八字码问题

    广度优先搜索算法(Breadth-First Search)又称为"宽度优先搜索""横向优先搜索"主要可以解决两类问题:

    ·是否存在从起点到终点的路径

    ·找出从起点到终点的最短路径a

    算法描述:

     1 int bfs(){
     2     初始化 首结点入队(首指针为0,尾指针为1)
     3     while(首指针小于尾指针){
     4         首指针后移一位
     5         for(;;) {    产生子结点
     6             if() {    判断子结点是否满足条件 
     7                 if(){    如果结点未与之前产生过重复 
     8                     尾指针后移
     9                     存入新的结点 (提供结点的父结点和层数)
    10                     if(){    如果新结点是目标节点 
    11                         输出并退出 
    12                     } 
    13                 } 
    14             }
    15         }
    16     } 
    17 }

    值得注意的是,广搜的过程中每生成一个子结点,都要提供指向他们的父结点的指针层数,以免找到解的时候找不到来时的路径。

    广度优先搜索在访问完该层的所有结点后,才开始访问下一层

     

     

    关于从搜索树通过队列存入数组的思想

    广搜一定要将生成的结点与之前的比较,以免出现重复的节点,浪费时间和空间,还有可能陷入死循环!

    八字码问题

    Description

    The 15-puzzle has been around for over 100 years; even if you don't know it by that name, you've seen it. It is constructed with 15 sliding tiles, each with a number from 1 to 15 on it, and all packed into a 4 by 4 frame with one tile missing. Let's call the missing tile 'x'; the object of the puzzle is to arrange the tiles so that they are ordered as:

     1  2  3  4
     5  6  7  8
     9 10 11 12
    13 14 15  x


    where the only legal operation is to exchange 'x' with one of the tiles with which it shares an edge. As an example, the following sequence of moves solves a slightly scrambled puzzle:

     1  2  3  4    1  2  3  4    1  2  3  4    1  2  3  4
     5  6  7  8    5  6  7  8    5  6  7  8    5  6  7  8
     9  x 10 12    9 10  x 12    9 10 11 12    9 10 11 12
    13 14 11 15   13 14 11 15   13 14  x 15   13 14 15  x
               r->           d->           r->


    The letters in the previous row indicate which neighbor of the 'x' tile is swapped with the 'x' tile at each step; legal values are 'r','l','u' and 'd', for right, left, up, and down, respectively.

    Not all puzzles can be solved; in 1870, a man named Sam Loyd was famous for distributing an unsolvable version of the puzzle, and
    frustrating many people. In fact, all you have to do to make a regular puzzle into an unsolvable one is to swap two tiles (not counting the missing 'x' tile, of course).

    In this problem, you will write a program for solving the less well-known 8-puzzle, composed of tiles on a three by three
    arrangement.

    Input

    You will receive a description of a configuration of the 8 puzzle. The description is just a list of the tiles in their initial positions, with the rows listed from top to bottom, and the tiles listed from left to right within a row, where the tiles are represented by numbers 1 to 8, plus 'x'. For example, this puzzle

     1  2  3
     x  4  6
     7  5  8


    is described by this list:


     1 2 3 x 4 6 7 5 8

    Output

    You will print to standard output either the word ``unsolvable'', if the puzzle has no solution, or a string consisting entirely of the letters 'r', 'l', 'u' and 'd' that describes a series of moves that produce a solution. The string should include no spaces and start at the beginning of the line.

    Sample Input

     2  3  4  1  5  x  7  6  8

    Sample Output

    ullddrurdllurdr

    这道题的思路大概是这样子

     1.读入到a数组内 
     2.输出一下a数组 做一个子程序 print2
     3.定义一下宽搜的节点
        1)首先有一个a棋盘(是一个二维数组)
        2)移动空位,记住空位的位置x,y
        3)记录深度(你走了几步)叫dep
        4)父节点,打方案用,走法dd
     4.定义队列大数组结构体data1 100000 一个头指针op,尾指针cl 
     5.偏移量,空位的移动方案d[5]结构体d[1].x d[1].y偏移
     6.初始化队列 首节点入队(初始状态) 写一个拷贝子程序
     7.做一个棋盘之间的复制 copy(a,b) 把a棋盘的值拷贝到b棋盘
     8.判断两个棋盘的值是否相同

     9.判断队列中棋盘是否存在 exist(a) a棋盘在我的data1数组中从1到cl是否存在
     10.输出方案的子程序print1
     11.写宽搜的框架

    但这个时候问题来了,单项搜索的效率太低了!!!如果要走20步,程序需要运行8秒多!!!

    这个时候,我们会发现题目中给出了终态 在这种情况下,同样的搜索从初态和终态出发各搜索一半状态,产生两棵深度减半的搜索树,在中间交会组合成最终的答案。

    这种算法的时间复杂度只有O(2N/2log2n/2)=O(N*2N/2)!!!

    这道题是不是完美地解决了??

    等等! 还有unsolvable的判断!但是这道题不能用搜索来判断是否有解------无解的时候程序将无限的搜索下去!!

    这个时候就需要事先判断八个数字的排列了。

    我们会用到置换:

    组合数学中,置换一词的传统意义是一个有序序列,其中元素不重复,但可能有阙漏。例如1,2,4,3可以称为1,2,3,4,5,6的一个置换,但是其中不含5,6。此时通常会标明为n个对象取r个对象的置换

    轮换长度为偶数的轮换称为偶轮换,反之则为奇轮换;由此可定义任一置换的奇偶性,并可证明:一个置换是偶置换的充要条件是它可以由偶数个换位生成。偶轮换在置换群中构成一个正规子群,称为交错群

    (摘自百度百科)

    也就是说,我们可以统计排列中逆序的次数(即后一项小于前一项,0不计),如果有奇数次,则无解。

    举个栗子叭

    1

    2

    3

    5

    4

    7

    0

    8

    6

    4<5 , 6<7 , 6<8 一共有三次逆序,所以无解.

    双向搜索代码

      1 #include<cstdio> 
      2 #include<iostream> 
      3 #include<string>
      4 #include<cstring>
      5 using namespace std;
      6 int a[4][4]={},c[4][4]={},x1,y1,x,y,op=0,cl=1,op1=0,cl1=1,dep=0,dep1=0; //op1 cl1表示由答案开始搜索的队列 
      7 int ii,jj;
      8 int a1[10]={}; //用于判断unsolvable 
      9 char k,answer[100];
     10 int ans[4][4]={{1,2,3,0},{4,5,6,0},{7,8,0,0},{0,0,0,0}};
     11 void input(int a[4][4]){
     12     for(int i=0;i<3;i++){
     13         for(int j=0;j<3;j++){
     14             cin>>k;
     15             if(k=='x') {a[i][j]=0;x=i;y=j;}
     16             else {
     17                 a[i][j]=k-'0';
     18                 a1[i*3+j]=a[i][j];
     19             }
     20         }
     21     }
     22 }
     23 void print2(int a[4][4]){
     24     for(int i=0;i<3;i++){
     25         for(int j=0;j<3;j++)
     26              printf("%d ",a[i][j]);
     27     printf("
    ");    
     28     }
     29 }
     30 void copy(int a[4][4],int b[4][4]){
     31     for(int i=0;i<3;i++){
     32         for(int j=0;j<3;j++)
     33              b[i][j]=a[i][j];    
     34     }
     35 }
     36 bool same(int a[4][4],int b[4][4]){
     37     for(int i=0;i<3;i++){
     38         for(int j=0;j<3;j++)
     39              if(b[i][j]!=a[i][j]) return false;    
     40     }
     41     return true;
     42 }
     43 
     44 struct node1{
     45     int x,y;
     46 }d[5]={{0,0},{-1,0},{+1,0},{0,-1},{0,+1}};
     47 struct node2{
     48     int m[4][4];
     49     int dep; //结点深度 
     50     int dd; //行走方向 
     51     int x,y; //坐标 
     52     int fa; //父结点下标 
     53 }data1[100001];
     54 struct node3{
     55     int m[4][4];
     56     int dep; //结点深度 
     57     int dd; //行走方向 
     58     int x,y; //坐标 
     59     int fa; //父结点下标 
     60 }data2[100001];
     61 bool exist(int a[4][4]){
     62     for(int i=0;i<=cl;i++){
     63         if(same(data1[i].m,a)) return false;
     64     }
     65     return true;
     66 }
     67 bool exist1(int a[4][4]){
     68     for(int i=0;i<=cl1;i++){
     69         if(same(data2[i].m,a)) return false;
     70     }
     71     return true;
     72 } 
     73 void print1(int cl, int cl1){
     74     int kk=cl,num=0;
     75     while(dep--){
     76         if(data1[kk].dd==1) answer[num++]='u';
     77         else if(data1[kk].dd==2) answer[num++]='d';
     78         else if(data1[kk].dd==3) answer[num++]='l';
     79         else if(data1[kk].dd==4) answer[num++]='r';    
     80         kk=data1[kk].fa;
     81     }
     82     for(int i=num-1;i>=0;i--) printf("%c",answer[i]);
     83     kk=cl1; num=0;
     84     while(dep1--){
     85         if(data2[kk].dd==1) answer[num++]='u';
     86         else if(data2[kk].dd==2) answer[num++]='d';
     87         else if(data2[kk].dd==3) answer[num++]='l';
     88         else if(data2[kk].dd==4) answer[num++]='r';    
     89         kk=data2[kk].fa;
     90     }
     91     for(int i=0;i<num;i++) printf("%c",answer[i]);
     92     printf("
    ");
     93 }
     94 
     95 int main(){
     96 //    freopen("Eight.in","r",stdin);
     97 //输入
     98     input(a);
     99 //特判
    100     if(same(a,ans)){
    101         printf("%d
    ",0);
    102         return 0;
    103     }
    104     int sum1=0;
    105     for(int i=1;i<9;i++)
    106         if (a1[i]!=0){
    107             for (int j=i+1; j<9; ++j)
    108                 if (a1[i]>a1[j] && a1[j]!=0)  {
    109                     ++sum1; 
    110                 }
    111         }
    112         
    113     if (sum1%2==0){
    114         printf("unsolvable
    ");
    115         return 0;
    116     }
    117     
    118 //首结点入队
    119     data1[1].dd=0;
    120     data1[1].dep=1;
    121     data1[1].x=x;
    122     data1[1].y=y;
    123     data1[1].fa=-1; 
    124     copy(a,data1[1].m);
    125 
    126     data2[1].dd=0;
    127     data2[1].dep=1;
    128     data2[1].x=2;
    129     data2[1].y=2;
    130     data2[1].fa=-1; 
    131     copy(ans,data2[1].m);
    132 //宽搜 
    133     while(op<=cl){
    134 
    135         op++; op1++; 
    136         if(dep!=data1[op].dep){
    137             for(int i=1;i<=cl;i++){
    138                 for(int j=1;j<=cl1;j++){
    139                     if(data1[i].dep==dep){
    140                         if(same(data1[i].m,data2[j].m)){
    141                             print1(i,j); return 0;
    142                         }
    143                     }
    144                 }
    145             }
    146         }
    147         dep=data1[op].dep;
    148         for(int i=1;i<=4;i++){
    149             x=data1[op].x+d[i].x;
    150             y=data1[op].y+d[i].y;
    151             if(x>=0 && x<3 && y>=0 && y<3){
    152                 //printf("
    %d %d %d
    ",op,x,y);
    153                 //移动后的棋盘存给c
    154                 copy(data1[op].m,c);
    155                 c[data1[op].x][data1[op].y]=c[x][y];
    156                 c[x][y]=0;
    157                 if(exist(c)){
    158                     cl++;
    159                     data1[cl].dd=i;
    160                     data1[cl].dep=dep+1;
    161                     data1[cl].x=x;
    162                     data1[cl].y=y;
    163                     data1[cl].fa=op; 
    164                     copy(c,data1[cl].m);
    165                 }
    166             }
    167         }
    168         dep1=data2[op1].dep;
    169         for(int i=1;i<=4;i++){
    170             x=data2[op1].x+d[i].x;
    171             y=data2[op1].y+d[i].y;
    172             if(x>=0 && x<3 && y>=0 && y<3){
    173                 copy(data2[op1].m,c);
    174                 c[data2[op1].x][data2[op1].y]=c[x][y];
    175                 c[x][y]=0;
    176                 if(exist1(c)){
    177                     cl1++;
    178                     data2[cl1].dd=i;
    179                     data2[cl1].dep=dep1+1;
    180                     data2[cl1].x=x;
    181                     data2[cl1].y=y;
    182                     data2[cl1].fa=op; 
    183                     copy(c,data2[cl1].m);
    184                 }
    185             }
    186         }
    187     } 
    188     return 0;
    189 } 
  • 相关阅读:
    HBase(2) Java 操作 HBase 教程
    HBase(1) 基本入门篇
    MongoDB 谨防索引seek的效率问题
    MongoDB一次节点宕机引发的思考(源码剖析)
    MongoDB-系统时钟跳变引发的风波
    是什么造成了数据库的卡顿
    了解 MongoDB 看这一篇就够了
    Reactive(3)5分钟理解 SpringBoot 响应式的核心-Reactor
    Reactive(2) 响应式流与制奶厂业务
    suanec-rotatelogs
  • 原文地址:https://www.cnblogs.com/hnoi/p/10922680.html
Copyright © 2011-2022 走看看