深度优先搜索(DFS)
深搜在无减枝的情况下,一般称之为 暴力搜索 ,其时间复杂度极高,
形象地说,一条路走到黑,一直走到走不通了再回到上一个结点然后继续向下走,直到走完整张图!
深搜需要遍历整张图,多用来解决求问题有多少个解、多少条路径、最大路径…等相关问题
深搜比较符合递归思想,所以多用递归来实现。
广度优先搜索(BFS)
与深搜相比,广搜一会一条路走到黑,而是一段一段地试探。即先遍历完与当前结点相邻的结点 or 子结点 ,再遍历深一层的结点,层层递进,直到找到想要的答案。
广度优先搜索一般用队列实现,主要用来解决 无权图的最短路径等问题。在一层层遍历的,所以第一次抵达目标结点的路径就是最短路径。
区别
深搜不在乎目的是什么,一条道一条道地游遍这个世界,欣赏所有的风景,最后给出你要的答案。
广搜一开始就目的明确,知道自己要的是什么,达到目的,交出答案,结束这场旅程!
DFS例题:八皇后问题
传说这是连数学王子高斯都算不正确的题目,如果能算出来,那么就比高斯都厉害了!
题目描述
会下国际象棋的人都很清楚:皇后可以在横、竖、斜线上不限步数地吃掉其他棋子。如何将8个皇后放在棋盘上(有8 * 8个方格),使它们谁也不能被吃掉!即任何两个皇后都不会在同一行、同一列、或同一斜线上。这就是著名的八皇后问题。
关于输入
本题没有输入
关于输出
输出八皇后问题所有的解,解用一个 88 的字符方正表示,如果某个位置没有皇后,则对应的字符为“.”,如果有皇后,则对应的字符为“”。为了美观起见,同一行的两个字符之间有一个空格,解的前一行是一个数字,表示这个解的序号。(详见例子输出)解要按照皇后在第 1 列、第 2 列……第 8 列出现的行数从小到大依次排序。
例子输入
(无)
例子输出
1
*. . . . . . .
. . . . . . * .
. . . . * . . .
. . . . . . . *
. * . . . . . .
. . . * . . . .
. . . . . * . .
. . * . . . . .
2
*. . . . . . .
. . . . . . * .
. . . * . . . .
. . . . . * . .
. . . . . . . *
. * . . . . . .
. . . . * . . .
. . * . . . . .………………
92
. . * . . . . .
. . . . . * . .
. . . * . . . .
. * . . . . . .
. . . . . . . *
. . . . * . . .
. . . . . . * .
*. . . . . . .
AC代码
#include<iostream>
using namespace std;
const int n = 8;
char a[n][n] = {};//棋盘
bool b[n], c[2 * n - 1], d[2 * n - 1];//表示这一列、两个对角线有没有皇后
int cnt = 0;//计数器,用于记录有多少种解法
bool inline check(int x, int y)
{
return !b[y] && !c[x + y] && !d[x - y + n - 1];//加上偏置n-1使下标为非负
}
void dayin()
{
cout << cnt << endl;
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
{
cout << a[j][i] ; // j表示行,i表示列
if (j != n - 1) cout << ' ';
}
cout << endl;
}
}
void dfs(int x)
{
if (x == n) //找到了一种摆法
{
cnt++;
dayin();
return;
}
for (int i = 0; i < n; i++)
{
if (check(x, i))
{
b[i] = c[x + i] = d[x - i + n - 1] = true;
a[x][i] = '*';
dfs(x + 1);
//回溯
b[i] = c[x + i] = d[x - i + n - 1] = false;
a[x][i] = '.';
}
}
}
int main() {
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
a[i][j] = '.';
dfs(0);//从0行开始摆放;
return 0;
}
思路整理
开一个数组 a 在模拟棋盘的同时,可以用 a[i][j] 来表示 第 i 行的皇后放在第 j 列。
用一个数组 b 来表示这一列是否可以用来放置皇后,当为 true 时,表示可以用来放置,为flase时表示不能放置皇后。
再开两个数组 c d 分别用来判断皇后对角线是否可用,根据观察可以发现,该皇后左对角线的行列关系都符合 i+j = 该行+该列;右对角线的行列关系符合 i-j = 改行 - 该列;
之后直接运用dfs即可。
BFS例题:奇怪的电梯
题目描述
呵呵,有一天我做了一个梦,梦见了一种很奇怪的电梯。大楼的每一层楼都可以停电梯,而且第 i i i 层楼( 1 ≤ i ≤ N 1 \le i \le N 1≤i≤N)上有一个数字 K i K_i Ki( 0 ≤ K i ≤ N 0 \le K_i \le N 0≤Ki≤N)。电梯只有四个按钮:开,关,上,下。上下的层数等于当前楼层上的那个数字。当然,如果不能满足要求,相应的按钮就会失灵。例如: 3 , 3 , 1 , 2 , 5 3, 3, 1, 2, 5 3,3,1,2,5 代表了 K i K_i Ki( K 1 = 3 K_1=3 K1=3, K 2 = 3 K_2=3 K2=3,……),从 1 1 1 楼开始。在 1 1 1 楼,按“上”可以到 4 4 4 楼,按“下”是不起作用的,因为没有 − 2 -2 −2 楼。那么,从 A A A 楼到 B B B 楼至少要按几次按钮呢?
输入格式
共二行。
第一行为三个用空格隔开的正整数,表示 N , A , B N, A, B N,A,B( 1 ≤ N ≤ 200 1 \le N \le 200 1≤N≤200, 1 ≤ A , B ≤ N 1 \le A, B \le N 1≤A,B≤N)。
第二行为 N N N 个用空格隔开的非负整数,表示 K i K_i Ki。
输出格式
一行,即最少按键次数,若无法到达,则输出
-1
。
样例输入 #1
5 1 5
3 3 1 2 5
样例输出 #1
3
提示
对于 100 % 100 \% 100% 的数据, 1 ≤ N ≤ 200 1 \le N \le 200 1≤N≤200, 1 ≤ A , B ≤ N 1 \le A, B \le N 1≤A,B≤N, 0 ≤ K i ≤ N 0 \le K_i \le N 0≤Ki≤N。
AC代码
#include<bits/stdc++.h>
using namespace std;
int n, a, b, k[205];
bool visited[205];
struct node {
int id, step;
node(int id_, int step_)
{
id = id_; step = step_;
}
node()
{
id = step = -1;
}
};//id表示楼层号,step表示到达该楼层按按钮次数
queue<node> q;
node x;
int main()
{
cin >> n >> a >> b;
for (int i = 1; i <= n; i++) cin>>k[i];
node root(a,0);
q.push(root);
while (!q.empty())
{
x = q.front(); q.pop();
if (x.id == b)
break;
if (x.id + k[x.id] <= n && !visited[x.id + k[x.id]])
{
node temp(x.id + k[x.id], x.step + 1);
q.push(temp);
visited[x.id + k[x.id]] = 1;
}
if (x.id - k[x.id] >= 1 && !visited[x.id - k[x.id]])
{
node temp(x.id - k[x.id], x.step + 1);
q.push(temp);
visited[x.id - k[x.id]] = 1;
}
}
if (x.id == b)
cout << x.step;
else
cout << -1;
return 0;
}
思路整理
本题可以看做是一个无权有向图,用广度优先搜索遍历,遍历过程中,所遇见的第一个结果即为最短路径,再用队列存储即可。