深度优先搜索:
就是从初始状态出发,下一步可能有多种状态;选其中一个状态深入,到达新的状态;直到无法继续深入,回退到前一步,转移到其他状态,然后再深入下去。最后,遍历完所有可以达到的状态,并得到最终的解。
1.相比广度优先搜索(BFS)的DFS的代码要短的多。拿一道很经典的搜索题来说
hdu 1312 Red and Black
描述
有一间长方形的房子,地上铺了红色、黑色两种颜色的正方形瓷砖。你站在其中一块黑色的瓷砖上,只能向相邻的黑色瓷砖移动。请写一个程序,计算你总共能够到达多少块黑色的瓷砖。
输入
包括多个数据集合。每个数据集合的第一行是两个整数W和H,分别表示x方向和y方向瓷砖的数量。W和H都不超过20。在接下来的H行中,每行包括W个字符。每个字符表示一块瓷砖的颜色,规则如下
1)‘.’:黑色的瓷砖;
2)‘#’:白色的瓷砖;
3)‘@’:黑色的瓷砖,并且你站在这块瓷砖上。该字符在每个数据集合中唯一出现一次。
当在一行中读入的是两个零时,表示输入结束。
输出
对每个数据集合,分别输出一行,显示你从初始位置出发能到达的瓷砖数(记数时包括初始位置的瓷砖)。
样例输入
6 9 ....#. .....# ...... ...... ...... ...... ...... #@...# .#..#. 0 0
样例输出
45
思路:
这道题BFS和DFS都可以用而且思路都很简单,只要有路就往下走,要是没路的话返回上一步换个方向继续走,直到不能走为止,此题的话用DFS的方法代码要短很多。
代码如下:
一、BFS
#include <iostream>
#include <queue>
using namespace std;
char room[23][23];
int dir[4][2] = {
{-1,0}, //向左
{0,-1}, //向上
{1,0}, //向右
{0,1} //向下
};
int Wx, Hy, num; //Wx行,Hy列 用num统计可走的位置有多少
#define CHECK(x,y) (x<Wx&&x>=0&&y>=0&&y<Hy) //是否在room中
struct node { int x, y; };
void BFS(int dx, int dy) {
num = 1;
queue<node>q; //队列中放坐标点
node start, next;
start.x = dx;
start.y = dy;
q.push(start);
while (!q.empty()) {
start = q.front();
q.pop();
//cout <<"out"<<start.x<<start,y<<endl;
for (int i = 0; i < 4; i++) { //按左、上、右、下4个方向顺时针逐一搜索
next.x = start.x + dir[i][0];
next.y = start.y + dir[i][1];
if (CHECK(next.x, next.y) && room[next.x][next.y] == '.') {
room[next.x][next.y] = '#'; //进队之后标记为已经处理过
num++;
q.push(next);
}
}
}
}
int main() {
int x, y, dx, dy;
while (cin >> Wx >> Hy) { //Wx行、Hy列
if (Wx == 0 && Hy == 0) //结束
break;
for (y = 0; y < Hy; y++) { //有Hy列
for (x = 0; x < Wx; x++) { //一次读入一行
cin >> room[x][y];
if (room[x][y] == '@') { //读入起点
dx = x;
dy = y;
}
}
}
num = 0;
BFS(dx, dy);
cout << num << endl;
}
return 0;
}
二、DFS
#include <iostream>
using namespace std;
int Wx, Hy;
int num;
char room[23][23];
#define CHECK(x,y) (x>=0&&x<Wx&&y>=0&&y<Hy)
int dir[4][2] = { {-1,0},{0,-1},{1,0},{0,1} };
void dfs(int dx, int dy)
{
room[dx][dy] = '#';
num++;
for (int i = 0; i < 4; i++)
{
int newx = dx + dir[i][0];
int newy = dy + dir[i][1];
if (CHECK(newx, newy) && room[newx][newy] == '.')
{
dfs(newx, newy);
}
}
}
int main()
{
int dx, dy, x, y;
while (cin >> Wx >> Hy) {
if (Wx == 0 && Hy == 0)
break;
for (y = 0; y < Hy; y++)
{
for (x = 0; x < Wx; x++)
{
cin >> room[x][y];
if (room[x][y] == '@')
{
dx = x;
dy = y;
}
}
}
num = 0;
dfs(dx, dy);
cout << num << endl;
}
return 0;
}
两种方法的主函数都是相同的,相比之下可见用DFS写的话代码要短很多,不过个人感觉BFS更好理解
2.对于DFS来说有一个不方便的地方就是:因递归列举出所有的路径可能会因为数量太大而超时,由于很多结点是不符合条件的,故需要用到“回溯”,也就是“看到不对头就撤退”,中途停止并返回。以下题为例
poj 2488 A Knight's Journey
描述
背景
骑士厌倦了一遍又一遍地看到相同的黑白方块,并决定
环游世界。每当一个骑士移动时,它是一个方向上的两个方格和一个垂直于这个方向的方格。骑士的世界就是他赖以生存的棋盘。我们的骑士住在一个棋盘上,棋盘的面积比普通的 8 * 8 棋盘要小,但它仍然是矩形的。你能帮助这个冒险的骑士制定旅行计划吗?
问题
找到一条路径,使骑士访问每个方格一次。骑士可以在棋盘的任何方格开始和结束。
输入
输入以第一行的正整数 n 开头。以下几行包含 n 个测试用例。每个测试用例由一行包含两个正整数 p 和 q 组成,使得 1 <= p * q <= 26。这表示 ap * q 棋盘,其中 p 描述了多少个不同的平方数 1, 。. . , p 存在, q 描述存在多少个不同的方形字母。这些是拉丁字母表的前 q 个字母:A、. . .
输出
每个场景的输出都以包含“Scenario #i:”的行开始,其中 i 是从 1 开始的场景编号。然后打印一行,其中包含按字典顺序访问棋盘所有方格的第一条路径,然后是骑士移动由空行。路径应该通过连接访问过的方块的名称在一行中给出。每个方块名称由一个大写字母后跟一个数字组成。
如果不存在这样的路径,您应该在一行上输出不可能。
样例输入
3
1 1
2 3
4 3
样例输出
Scenario #1: A1 Scenario #2: impossible Scenario #3: A1B3C1A2B4C2A3B1C3A4B2C4
思路:
任选一个起点,按照国际象棋马的跳法,不重复的跳完整个棋盘,如果有多种路线则选择字典序最小的路线(路线是点的横纵坐标的集合,注意棋盘的横坐标的用大写字母,纵坐标是数字)
首先要明白这个题的意思是能否只走一遍(不回头不重复)将整个地图走完,而普通的深度优先搜索是一直走,走不通之后沿路返回到某处继续深搜。所以这个题要用到的回溯思想,如果不重复走一遍就走完了,做一个标记,算法停止;否则在某种DFS下走到某一步时按马跳的规则无路可走而棋盘还有为走到的点,这样我们就需要撤消这一步,进而尝试其他的路线(当然其他的路线也可能导致撤销),而所谓撤销这一步就是在递归深搜返回时重置该点,以便在当前路线走一遍行不通换另一种路线时,该点的状态是未访问过的,而不是像普通的DFS当作已经访问了。
代码如下:
#include <stdio.h>
#include <string.h>
#include <iostream>
using namespace std;
int book[10][10];
int arr[8][2] = { {-1,-2},{1,-2},{-2,-1},{2,-1},{-2,1},{2,1},{-1,2},{1,2} };//八个方向,
//根据途中马的位置,因为是字典序,所以它必须先走左上角,所以这八个方向左边优先
int a, b;
int res[100][2];//用来存xy是第几步走的
int found;//找到一个可以执行通道的标志
#define check(x,y) (x < 0 || x >= a || y < 0 || y >= b)
void dfs(int m, int n, int depth)
{
res[depth][0] = m;
res[depth][1] = n;
if (depth == a * b)
{
found = 1;
for (int i = 1; i <= a * b; i++)
{
printf("%c%d", res[i][1] + 'A', res[i][0] + 1);
}
cout << endl;
return;
}
int k;
int tx, ty;
for (k = 0; k < 8; k++)
{
tx = m + arr[k][0];
ty = n + arr[k][1];
if (check(tx,ty)||book[tx][ty] != 0)
continue;
if (found)
break;
book[tx][ty] = 1;
dfs(tx, ty, depth + 1);
book[tx][ty] = 0;
}
}
int main()
{
int n;
cin >> n;
int count = 0;
while (n--)
{
memset(book, 0, sizeof(book));
found = 0;
count++;
cin >> a >> b;
cout << "Scenario #" << count << ":" << endl;
book[0][0] = 1;
dfs(0, 0, 1);
if (!found)
{
cout << "impossible" << endl;
}
if (n != 0)
cout << endl;
}
}
大数据 liyang