ACM算法:广度优先搜索

来源:互联网 发布:java泛型 编辑:程序博客网 时间:2024/06/05 08:39

广度优先搜索的用途:广度优先搜索和深度优先搜索所针对的题型很相似,比如迷宫型问题、数字列问题以及状态

搜索等问题。但是它和深度优先搜索的区别在于,深度优先搜索的适用范围更加广泛,因为深度优先搜索存在局限

性,有些题目适用深度优先搜索算法可能会出现“深度无限”的问题,这将导致可怕的无限循环。


但这并不代表深度优先搜索算法比广度差,由于广度优先搜索需要适用队列储存所有点的状态,这样会比深度更加

消耗空间,在题目数据相对较大时,就容易超内存。而所以深度优先搜索更加适用于数据规模较大的情况,当然一切

都还要根据具体的情况而定。


广度优先搜索还有一种叫做状态压缩的问题,由于这个问题也是一种较难理解的思想,我会单独用一篇博客进行讲解,

请另见博客。


广度优先搜索的思路:了解过深度优先搜索的人应该知道,深度优先搜索的实现一般为栈、递归或者优先队列,而

广度优先搜索的实现常常用普通的队列,而且其思想较易于理解。


它的实际思想就是从起点出发,把起点周围方向上的点存入队列,在从队列头取出一个点,再将该店方向上的点存入

队列,重复存取操作,直到遇见符合条件的点则退出函数,或者一直没有符合条件的点,则直至队列为空,退出函数。


同深度优先搜索相同,你必须首先确定方向问题,比如在迷宫问题中,你需要用一个二维的4*2或者8*2数组表示四个

或八个方向,还需要使用数组或者其他什么方式标志已经遍历过的点。


例题解析:接下来我将用一道较简单的题进行讲解,加强理解

Knight Moves

Problem Description

A friend of you is doing research on the Traveling Knight Problem (TKP) where you are to find the shortest closed tour of knight moves that visits each square of a given set of n squares on a chessboard exactly once. He thinks that the most difficult part of the problem is determining the smallest number of knight moves between two given squares and that, once you have accomplished this, finding the tour would be easy.
Of course you know that it is vice versa. So you offer him to write a program that solves the "difficult" part. 

Your job is to write a program that takes two squares a and b as input and then determines the number of knight moves on a shortest route from a to b. 

Input

The input file will contain one or more test cases. Each test case consists of one line containing two squares separated by one space. A square is a string consisting of a letter (a-h) representing the column and a digit (1-8) representing the row on the chessboard. 

Output

For each test case, print one line saying "To get from xx to yy takes n knight moves.". 

Sample Input

e2 e4

a1 b2

b2 c3

a1 h8

a1 h7

h8 a1

b1 c3

f6 f6

Sample Output

To get from e2 to e4 takes 2 knight moves.

To get from a1 to b2 takes 4 knight moves.

To get from b2 to c3 takes 2 knight moves.

To get from a1 to h8 takes 6 knight moves.

To get from a1 to h7 takes 5 knight moves.

To get from h8 to a1 takes 6 knight moves.

To get from b1 to c3 takes 1 knight moves.

To get from f6 to f6 takes 0 knight moves.


题目大意:给定一个8*8数组,行数用“a-h”表示,列数用“1-8”表示,然后给出一个起点和终点要求计算出从

起点到终点的最短步数。需要注意的是,移动的方式是类型象棋中的马,按照“日”字移动,比如

1  0

0  0

0  1

1移动到1的这种方式


代码:

#include <iostream>#include <cstring>#include <queue>#include <algorithm>using namespace std;int row_x1, row_x2;//记录起点和终点的行数,数字表示int col_y1, col_y2;//记录起点和终点的列数,数字表示char char_x1, char_x2;//记录起点和终点的行数,字母表示int step;//记录步数bool mark[10][10];//用于记录点是否被遍历过int dir[8][2] = { {-2,1},{-2,-1},{-1,-2},{1,-2},{2,-1},{2,1},{1,2},{-1,2} };//方向struct Node{int x, y, step;};queue<Node> Q;void BFS(){Node now, next;while (!Q.empty())//每次调用函数,都要清空队列Q.pop();now.x = row_x1;now.y = col_y1;now.step = 0;Q.push(now);//压入结点while (!Q.empty()) {now = Q.front();Q.pop();for (int i = 0; i < 8; i++) {next.x = now.x + dir[i][0];next.y = now.y + dir[i][1];if (next.x < 0 || next.x >= 8 || next.y < 0//判断是否符合要求|| next.y >= 8 || mark[next.x][next.y])continue;mark[next.x][next.y] = true;//标记已经遍历过next.step = now.step + 1;//在前一步基础之上加一Q.push(next);//压入队列if (next.x == row_x2 && next.y == col_y2) {//符合要求则退出函数cout << "To get from " << char_x1 << col_y1 + 1<< " to " << char_x2 << col_y2 + 1 << " takes "<< next.step << " knight moves." << endl;return;}}}}int main(void){ios::sync_with_stdio(false);while (cin >> char_x1 >> col_y1 >> char_x2 >> col_y2) {row_x1 = char_x1 - 'a'; col_y1 -= 1;//将字母表示转换为数字表示row_x2 = char_x2 - 'a'; col_y2 -= 1;if (row_x1 == row_x2 && col_y1 == col_y2) {cout << "To get from " << char_x1 << col_y1 + 1<< " to " << char_x2 << col_y2 + 1 << " takes 0 knight moves." << endl;}else {//这里值得注意的是,二维数组的重置方式和一维数组的重置方式一样,用memset函数memset(mark, false, sizeof(mark));//每次都要重置标记数组BFS();}}return 0;}



总结:广度优先搜索的使用是ACM的基础,也在平时的训练和比赛中使用的相对广泛,所以需要我们好好掌握,在学习的过程中,

我相信你收获到的不只是算法,还有思想。



原创粉丝点击