广度优先搜索与八字码问题

广度优先搜索算法(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 } 
原文地址:https://www.cnblogs.com/hnoi/p/10922680.html