BFS-八数码问题与状态图搜索

99封情书 提交于 2020-01-23 00:03:38

  在一个3*3的棋盘上放置编号为1~8的八个方块,每个占一格,另外还有一个空格。与空格相邻的数字方块可以移动到空格里。任务1:指定的初始棋局和目标棋局,计算出最少的移动步数;任务2:数出数码的移动序列。

  把空格看成0,一共有九个数字。

  输入样例:

  1 2 3 0 8 4 7 6 5 

  1 0 3 8 2 3 7 6 5 

  输出样例:

  2

  1.把一个棋局看成一个状态图,总共有9!= 362880个状态。从初始棋局开始,每次移动转到下一个状态,达到目标棋局后停止。

  2.康托展开

    康托展开是一种特殊的哈希函数。其功能是在输入一个排列,计算出它在在全排列中从小到大排序的位次。

    eg:判断 2143是{1,2,3,4}的全排列中的位次。

      (1)首位小于2的所有排列。比2小的只有1,后面三个数的排列有3*2*1=3!个,写成1*3!=6

      (2)首位为2,第二位小于1的所有排列。无,写成0*2!=0

      (3)前两位为21,第三位小于4的所有排列。只有3一个数,写成1*1!=1

      (3)前三位为214,第四位小于3的所有排列。无,写成0*0!=0

      求和:1*3!+0*2!+1*1!+0*0!=7

    所以位次的计算公式为X = a[n]*(n-1)! +a[n-1]*(n-2)! + … + a[1]*0!

  

 1 #include<bits/stdc++.h>
 2 #include<queue>
 3 using namespace std;
 4 
 5 const int len = 362880; //状态共9! = 362880种
 6 int visited[len] = {0};//标记已有状态用来去重 
 7 int start[9];//起始状态 
 8 int goal[9];//目标状态 
 9 int factory[] = {1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362800};//0到9的阶乘 
10 int dir[4][2] = {{-1, 0}, {0, -1}, {1, 0}, {0, 1}};
11 
12 struct node{
13     int state[9];//棋局状态按一维存放下来 
14     int dis;//记录从起始状态移动到当前状态的步数 
15 };
16  
17 bool cantor(int str[], int n){
18     int result = 0;
19     for(int i=0; i<n; i++){
20         int cnt = 0;
21         for(int j=i+1; j<n; j++){
22             if(str[i] > str[j])
23                 cnt ++;
24         }
25         result += cnt+factory[n-i-1];
26     }
27     if(!visited[result]){
28         visited[result] = 1;
29         return 1;
30     }
31     return 0;
32 }
33 
34 int BFS(){
35     node head, next;
36     memcpy(head.state, start, sizeof(head.state));//复制起始状态并插入队列 
37     head.dis = 0; 
38     cantor(head.state, 9);
39     queue<node>q;
40     q.push(head);
41     
42     while(!q.empty()){
43         head = q.front();
44         q.pop();
45         int z;
46         for(z=0; z<9; z++)
47             if(head.state[z] == 0)//找到0 
48                 break;
49         int x = z % 3;//将0的一维位置转化为二维的横纵坐标 
50         int y = z / 3;
51         for(int i=0; i<9; i++){
52             int newx = x + dir[i][0];
53             int newy = y + dir[i][1];
54             int newz = newx + 3*newy;//将0移动后重新转化为一维坐标 
55             if(newx>=0 && newx<3 && newy>=0 && newy<3){//避免越界 
56                 memcpy(&next, &head, sizeof(struct node));
57                 swap(next.state[z], next.state[newz]);//复制原先状态后,改变0的位置 
58                 next.dis ++;
59                 if(memcmp(next.state, goal, sizeof(next.state)) == 0)
60                     return next.dis;
61                 if(cantor(next.state, 9))//查重 
62                     q.push(next);
63             } 
64         }
65     }
66     return -1;
67 }
68 
69 int main(){
70     for(int i=0; i<9; i++)
71         scanf("%d", start+i);
72     for(int i=0; i<9; i++)
73         scanf("%d", goal+i);
74     
75     int num = BFS();
76     if(num != -1)
77         printf("%d\n",num);
78     else
79         printf("Impossible\n"); 
80 }

  (1)用于存放状态图的以及步数的结构体

  (2)用于移动的数组

  (3)用于去重的标记数组

  (4)提前算好阶乘存放于数组中

  (5)康拓函数判重

  (6)BFS函数:queue<node>q;node head, next;

  (7)状态图中某数字方块的一维坐标和二维坐标的相互转化

  (8)检查坐标是否合法   

八数码问题多种解法:https://www.cnblogs.com/zufezzt/p/5659276.html

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!