八数码问题:C++广度搜索实现
毕竟新手上路23333,有谬误还请指正。 课程设计遇到八数码问题(这也是一坨),也查过一些资料并不喜欢用类函数写感觉这样规模小些的问题没有必要,一开始用深度搜索却发现深搜会陷入无底洞,如果设定了深度限制又会有很多情况无法找到,然后果断放弃,改用广度搜索。 如果要改善代码效率还可以用双向搜索,即从起始状态和最终状态同时搜索,找到相同状态,这样搜索时间会缩短一半。此外还可以用A*(启发式算法)会显得高端很多。
题目要求: 在一个3*3的棋盘上,随机放置1到8的数字棋子,剩下一个空位。数字可以移动到空位(编程时,空位可用0代替,且可以理解为是空位的上、下、左、右移动),经过若干次移动后,棋局到达指定目标状态。 数组表示棋局状态。用函数表示空格(0)的移动,使用函数具有前提条件,满足条件才可以使用。 用广度优先或深度优先搜索求解。还可以使用启发式求解,以提高搜索效率。 要求: ① 编程求解问题; ② 给出中间状态; ③ 给出解序列(函数调用序列)
1 #include <iostream> 2 #include <vector> 3 #include <string> 4 #include <queue> 5 #include <algorithm> 6 #include <cmath> 7 #include <ctime> 8 #include <cstdio> 9 #include <sstream> 10 #include <deque> 11 #include <functional> 12 #include <iterator> 13 #include <list> 14 #include <map> 15 #include <memory> 16 #include <stack> 17 #include <set> 18 #include <numeric> 19 #include <utility> 20 #include <cstring> 21 #include <fstream> 22 23 using namespace std; 24 int board[25]; //初始状态,一维数组模拟二维 25 int destboard[25]; //目标状态 26 int dx[] = {-1, 0, 1, 0}; //数字0的移动偏移量 27 int dy[] = {0, 1, 0, -1}; 28 map<int, bool> mark; //记录已搜索的状态 29 30 int lists(int i, int j) 31 { 32 return i*5 + j; //返回i, j二维数组的一维位置 33 } 34 35 int judge() //运算前判断是否可以找到一种对于初末状态可行的变换 36 { 37 int first_d[10],last_d[10],i,j=6,k,rank1=0,rank2=0; 38 for(i=1;i<=9;i++) //初状态赋值给first_d[10] 39 { 40 while(1) 41 { 42 if(j==9){j=11;} 43 if(j==14){j=16;} 44 first_d[i]=destboard[j]; 45 j++; 46 break; 47 } 48 } 49 j=1;i=1; 50 for(k=1;k<=9;k++) //最终状态赋值给last_d[10] 51 { 52 while(1) 53 { 54 last_d[k]=board[lists(i, j)]; 55 j++; 56 if(j==4){i++;j=1;} 57 break; 58 } 59 } 60 61 for(j=2;j<=9;j++) //计算初状态的奇偶性 62 { 63 for(i=1;i<j;i++) 64 { 65 if(first_d[i]>first_d[j]&&first_d[i]!=0&&first_d[j]!=0){rank1++;} 66 } 67 } 68 69 for(j=2;j<=9;j++) //计算末状态的奇偶性 70 { 71 for(i=1;i<j;i++) 72 { 73 if(last_d[i]>last_d[j]&&last_d[i]!=0&&last_d[j]!=0){rank2++;} 74 } 75 } 76 int a1=rank1%2,a2=rank2%2; 77 if(a1!=a2){return 0;} //判断奇偶性是否相同,相同才可以从出状态变到末状态 78 else{return 1;} 79 } 80 81 struct Stat //结构体三个参数 82 { 83 int step; // 步数 84 int board[25]; // 状态 85 Stat(int *b, int s=0) 86 { 87 memcpy(this->board, b, sizeof(int)*25); //对状态的赋值操作 88 step = s; 89 } 90 }; 91 92 bool ok(int *b) // 判断是否到已经达目标状态 93 { 94 for(int i=1; i<=3; ++i) 95 for(int j=1; j<=3; ++j) 96 { 97 if(b[lists(i, j)] != destboard[lists(i, j)]) 98 return false; 99 } 100 return true; 101 } 102 103 int Bfs() 104 { 105 int judge_first=judge(); 106 ofstream ofs; //建立数据外存文件 107 ofs.open("output.dat"); 108 if(judge_first==0){cout<<"不存在"<<endl;return 2333333;} 109 if(judge_first==1) 110 { 111 queue<Stat> que; //建队que 112 que.push(Stat(board, 0)); // 初始状态入队 113 while(que.size()) 114 { 115 int m=0, mk=1; // 记录状态m,以及基数mk 116 Stat p = que.front(); //取出队头元素 117 for(int i=1; i<=3; ++i) 118 { 119 for(int j=1; j<=3; ++j) 120 { 121 ofs<<p.board[lists(i, j)]<<" "; 122 } 123 ofs<<endl; 124 } 125 ofs<<endl; 126 127 que.pop(); //出队 128 129 if(ok(p.board)) 130 { 131 return p.step; // 到达目标则返回最短步数 132 } 133 134 for(int i=1; i<=3; ++i) // 这个是为了标记初始状态,不能遗漏 135 for(int j=1; j<=3; ++j) 136 { 137 m+=p.board[lists(i, j)]*mk; 138 mk*=10; 139 } 140 if(!mark.count(m)) // 未标记则标记 141 mark[m] = true; 142 143 for(int k=0; k<4; ++k) // 四个方向搜索 144 { 145 Stat n(p.board, p.step+1); // n是下一步搜索的状态 146 int zx, zy; // zx,zy存放当前状态0的位置 147 148 for(int i=1; i<=3; ++i) // 搜索当前状态的0的位置 149 for(int j=1; j<=3; ++j) 150 { 151 if(p.board[lists(i,j)]==0) 152 { 153 zx = i; 154 zy = j; 155 break; 156 } 157 if(p.board[lists(i,j)]==0) 158 break; 159 } 160 161 int nx = zx+dx[k]; //下一个状态的0的位置 162 int ny = zy+dy[k]; 163 m = 0; //标记状态 164 mk = 1; 165 swap(n.board[lists(nx,ny)],n.board[lists(zx, zy)]); //交换 166 167 for(int i=1; i<=3; ++i) 168 for(int j=1; j<=3; ++j) 169 { 170 m+=n.board[lists(i, j)]*mk; 171 mk*=10; 172 } 173 if(!mark.count(m)) 174 { 175 mark[m] = true; 176 que.push(n); //若未搜索过,则入队 177 } 178 } 179 } 180 ofs.close(); //结束外存 181 return 2333333; 182 } 183 return 0; 184 } 185 186 int main() 187 { 188 cout<<"广度搜索八数码问题:\n"; 189 cout<<"请输入初始状态用0-8表示\n"; 190 memset(board, 0, sizeof(board)); 191 for(int i=1; i<=3; ++i) 192 for(int j=1; j<=3; ++j) 193 scanf("%1d", &board[lists(i, j)]); 194 195 cout<<"请输入结束状态\n"; 196 for(int m=6;m<=8;m++){scanf("%d",&destboard[m]);} 197 for(int m=11;m<=13;m++){scanf("%d",&destboard[m]);} 198 for(int m=16;m<=18;m++){scanf("%d",&destboard[m]);} 199 mark.clear(); 200 201 cout<<"准备搜索...\n"; 202 system("pause"); 203 cout<<"搜索中.....\n"; 204 205 int t=Bfs(); 206 cout<<"搜索完毕,过程已经以文件的形式存储\n"; 207 if(t==2333333){cout<<"没有找到"<<endl;} 208 else{cout<<"深度为:"<<t<< endl;} 209 system("pause"); 210 return 0; 211 }
输入初始状态和最终状态,中间过程会生成在”output.dat”中。