传统弱校HFUT的蒟蒻,真相只有一个

八数码问题: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”中。

posted @ 2015-06-28 04:04  未名亚柳  阅读(1646)  评论(0编辑  收藏  举报