zoj - 1091 - Knight Moves(广度优先法)
这个问题的难处,对我而言在于计步数,开始时想在for(i = 0; i < 8; i++)后来个cnt++计步数,事实证明,那是错误的,因为这样的话每一层的每个元素自己往8个方向移动后,都有个cnt++,而我们每层只应使cnt自加一次就够,于是,将每层最右边的结点保存下来,判断是每层的最右结点再加1。另一个问题是队列的清零问题,没的话,上一组测试数据将影响下一组测试数据。
#include <iostream> #include <string.h> #include <queue> using namespace std; typedef struct Tdata //定义结点数据类型 { int x; int y; }data; int s[8][8]; //结点表:a1 对应 s[0][0],a8 对应 s[0][7] int vis[8][8]; //状态标记,0为未访问过,1为访问过 int cnt; //步数计数器 int dx[] = {-1, -2, -2, -1, 1, 2, 2, 1}; //与dy一起组成移动间距 int dy[] = {-2, -1, 1, 2, 2, 1, -1, -2}; //与dx一起组成移动间距 queue<data> qu; //广度优先遍历中用的队列 int bfs(data a, data b) //a为起始点,b为目标点 { int i; if(a.x == b.x && a.y == b.y) //如果起始点已是目标点,返回步数 return cnt; vis[a.x][a.y] = 1; //将起始点标记为已访问 qu.push(a); //入列 data rear = a, right; //rear为应该使步数+1时,正在遍历的结点, right用来保存每层遍历最右边的结点 while(!qu.empty()) { data temp = qu.front(); //取队头元素 qu.pop(); //出列 for(i = 0; i < 8; i++) //对8个方向进行遍历 { data newnode; //朝某个方向去时的新结点 newnode.x = temp.x + dx[i]; newnode.y = temp.y + dy[i]; if(newnode.x == b.x && newnode.y == b.y) //若已到达目标结点,返回步数 return cnt+1; //否则,判断移动有没有出界,是否已经遍历过 if(newnode.x >= 0 && newnode.x < 8 && newnode.y >= 0 && newnode.y < 8 && vis[newnode.x][newnode.y] == 0) { qu.push(newnode); //入列 right = newnode; //保存结点,只为了最后的每层最右边的那个点 vis[newnode.x][newnode.y] = 1; //修改其访问状态 } } if(temp.x == rear.x && temp.y == rear.y) //当正在访问的结点是所在层的最右结点时,步数+1 { cnt++; rear = right; //将新一层的最右结点存起来 } } return -1; //无法到达目标结点时,返回-1,虽然这种情况不会出现 } int main() { string a, b; data begin, end; while(cin>>a>>b) { memset(vis, 0, sizeof(vis)); //令状态数组的初始值为0,即未访问过 begin.x = a[0] - 'a'; //转换 begin.y = a[1] - '0' - 1; end.x = b[0] - 'a'; end.y = b[1] - '0' - 1; cnt = 0; //步数清零 cout<<"To get from "<<a<<" to "<<b<<" takes "<<bfs(begin, end)<<" knight moves."<<endl; while(!qu.empty()) //队列清空,这一步很重要,否则可能会使上次在qu中遗留下来的结点干扰现在的结果 { qu.pop(); } } return 0; }