<p>/* 因为注释很详细,就直接上代码了,需要注意的是,用了白书的三种方法来进行判重,其中最快捷的方法还是stl的set,还有哈希技术涉及到了多个链表的处理,还有一种就是编码解码技术,这个需要找到一个非常好的函数才能达到一一对应。而哈希表不需要一一对应(因为有链表)。</p><div>*/</div>// // main.cpp // EightBits // // Created by LinYuchen on 2/13/15. // Copyright (c) 2015 LinYuchen. All rights reserved. //八数码问题 暂时不用启发式(A*)只是想锻炼结点查找表(判重)的部分 //http://codevs.cn/problem/1225/ #include <iostream> #include <set> #include <string> #define MAXSTATE 10000 using namespace std; typedef int State[9];//把棋盘的九个位置存起来当做一个状态 State State goal;//用来存储最后想达到的状态 State st[MAXSTATE];//用来存储从启示到达终点的所有状态过程 实际上是个队列 因为是 bfs int dist[MAXSTATE];//用来存储每一个走到st里的每一个state都已经走了多少步 (why?) int d = 0; //进行坐标变换 分别是上下左右 int dx[]={-1,1,0,0},dy[]={0,0,-1,1}; int bfs();//宽度优先搜索来找到最短路径(图的最短路径) void init_lookup_table();//初始化查找表 bool try_to_insert(int rear);//尝试插入,进行判重如果已经走过就返回false不许插入队列 int getId(int x,int y){return (x-1)*3+y-1;}//第x行 第y列 x=1,2,3 y=1,2,3 int bfs(){ init_lookup_table(); int front=1,rear=2;//队列的头指针是1 尾指针是2(牢记:尾指针指向的是现有的最后一个元素的下一个位置) while (front<rear) {//front<rear 可以用来判断队列是否非空 State& s = st[front];//此时的s表示的是此时的队首状态,也是即将进行发生变化的 /*判断待处理的s是否正好就是goal 也就是达到了目的否? //如果已经找到了直接返回front 在结尾处我们可以通过front在st中找到goal*/ if(memcmp(goal, s, sizeof(State))==0) return front; //如果可以进行到这里 说明我们要进行移动空白格(0)了 //想移动空白格 首先要找到它 int i=0; for (;i<9;i++)if(!s[i]) break; int zero_x=i/3+1 , zero_y=i%3+1;//转成坐标 和白书不同 //开始进行四个方向的移动 需要进行出界判断 for (int t=3; t>=0; t--){ int newx = zero_x+dx[t],newy=zero_y+dy[t]; if(newx<=3 and newx>=1 and newy>=1 and newy<=3){ int new_zero = getId(newx,newy); //如果移动是合法的 就开始进行向队尾加入元素 State& r = st[rear]; //r是从s上移动而来的 所以只需要进行微调 memcpy(&r, &s, sizeof(State)); //swap(r[new_zero],r[i]);//i是s中0的位置 new_zero是0移动之后的位置 r[new_zero]=s[i];r[i]=s[new_zero]; dist[rear] = dist[front]+1;//front可以取到没有移动之前的距离 //尝试把移动之后的状态进行插入队列继续走,如果发现已经重复则不进行rear++ //rear++表示已经插入了队列,否则即使占领了st[rear]也会被下一次循环覆盖 if(try_to_insert(rear)) rear++; } } front++;//不管怎样都是处理完了一个~所以要出队 } //如果没有找到任何的路径那么就返回0 return 0; } //对应set的方式 init函数是 //利用stl的set进行判重 集合的互异性 set的元素类型必须重载 < 运算 所以有限考虑int set<int> vis; void init_lookup_table(){vis.clear();}//对vis集合进行清空 bool try_to_insert(int rear){ //首先要把state转换成一个一一对应的int 才能插入集合来判断 int id =0; for (int i=0; i<9; i++) id += st[rear][i] + id*10; //第一种检查方法是用find函数 和 end函数来进行比较 这里原理不是很清楚 //if(vis.find(id)==vis.end()) return false; //第二种用count函数来进行判断 依然不懂得原理 if(vis.count(id)!=0) return false; vis.insert(id); return true; } //对应编码解码的方法判重 bool vis[362880];int fact[9];//vis的长度是由9!确定的,fact[i]存的是i的阶乘 void init_lookup_table(){ memset(vis, false, sizeof(bool)); //初始化fact数组 保存每个数的阶乘 为了以后使用方便 fact[0]=1; for(int i=1;i<=8;i++) fact[i]=i*fact[i-1]; } bool try_to_insert(int rear){ //进行编码 编码之后看vis是否 int code = 0; for(int i =0;i<9;i++){ int cnt =0;//cnt是为了记录st[rear][i]后面有几个比他小的数 for (int j=i+1; j<9; j++) if(st[rear][j]<st[rear][i]) cnt++; code += fact[8-i]*cnt;//编码方式比较奇怪 } //code是个 sigma(i=0-8) (8-i)!*第i个数后面比它小的数的个数。 if (vis[code]) return false; vis[code]=true; return true; } //最优方式 hash链表技术 const int MAXHASHSTATE = 1000003;//这个常数是hash值的范围(最大值) int head[MAXHASHSTATE],nextState[MAXSTATE]; //head的下表是hash值,我们可以通过下标(也就是hash值)去访问这个hash值所对应的state //也就是说head数组里的每个值其实是st数组的下标 //next是链表 一条链是同一哈希值 一个state接一个state 所以next数组的下标和值都是st的下标 int getHash(int t){//返回哈希值 int hash_value = 0; for (int i=0; i<9; i++) hash_value += hash_value*10+st[t][i]; return hash_value%MAXHASHSTATE; } void init_lookup_table(){ memset(head, 0, sizeof(int)); memset(nextState, 0, sizeof(int));} bool try_to_insert(int rear){ int hv = getHash(rear); int u = head[hv];//找到此哈希值对应的state 没有就是0 如果有就是这条哈希链的首个 while(u){//循环地去看这条链子 if(memcmp(st[u], st[rear], sizeof(State))==0) return false;//重复 //如果不重复那么就在这个链子上继续寻找 u = nextState[u];//如果没有了u就会变成0 } //循环完整条链子 没有发现重复 那么就要在这条链子的头部插入当前元素 nextState[rear]=head[hv];//注意是插在头部,所以rear的下一个是当前的头部 head[hv]=rear; return true; } int main(int argc, const char * argv[]) { //初始化目标状态 // for (int i=0; i<8; i++) // goal[i]=i+1; // goal[8]=0; // char goal_str[] = "123804765"; for (int i=0; i<9; i++) { goal[i]=goal_str[i]-'1'+1; } //进行输入 State& start=st[1];//引用地址来进行改名字 主要是为了简化代码 dist[1]=0; //用1是为了配合bfs 0表示没有 char start_state[9]; cin>>start_state; for (int i=0; i<9; i++) start[i]=start_state[i]-'1'+1; d = dist[bfs()]; cout<<d<<endl; return 0; }