搜索算法——DFS、BFS(简单介绍)

  • 利用计算机的高性能,去穷举一个问题的部分或所有的可能情况从而求出问题的解的一种方法
  • 实际上是根据初始条件和扩展规则构造一棵解答树并寻找目标状态的节点的过程
  • 深搜是栈的方式(递归实现),而广搜是队列(层层扩展)

DFS

一般步骤

(1) 把初始状态放入数组中,设为当前状态;
(2) 扩展当前的状态,产生一个新的状态放入数组中,同时把新产生的状态设为当前状态;
(3) 判断当前状态是否和前面的重复,如果重复则回到上一个状态,产生它的另一状态;
(4) 判断当前状态是否为目标状态,如果是目标,则找到一个解答,结束算法。
(5) 如果数组为空,说明无解。

//层数固定的情况
void dfs(int k) {
    if(结束) {//打印、结束、比较
        做相应处理
    } else {
        for(...) {
            if() {满足条件
                设置条件    //存数组、设约束(已访问等)、加总量(求总和等)
                bfs(k+1)
                恢复条件设置
            }
        }
    }
}

经典问题

描述
给定整数 a 1 、 a 2 、 . . . . . . . a n a_{1}、a_{2}、.......a_{n} a1a2.......an,判断是否可以从中选出若干数,使它们的和恰好为K。

输入
首先, n n n k k k n n n表示数的个数, k k k表示数的和。 接着一行 n n n个数。
1 < = n < = 20 1<=n<=20 1<=n<=20,保证不超 i n t int int范围)

输出
如果和恰好可以为 k k k,输出“YES”,否则“NO”
样例输入

4 13
1 2 4 7

样例输出

YES
#include <cstdio>
#define MAXN 10005
int a[MAXN], n, k;
bool dfs(int k, int sum) {
    if(k == n+1)	return sum == k;
    if(dfs(k+1, sum + a[k]))	return true;
    if(dfs(k+1, sum))	return true;
    return false;
}
int main() {
    scanf("%d%d", &n, &k);
    for(int i = 1; i <= n; i++)
        scanf("%d", &a[i]);
    if(dfs(1, 0)) printf("YES\n");
    else printf("NO\n");
    return 0;
}

求全排列

void swap(int &a, int &b) {
	int temp;
	temp = a;
	a = b;
	b = temp;
}
void perm(int list[], int low, int high) {//从m到s 
	if(low == high) {//排列一次后的结果全部在list中 
		for(int i = 1; i <= low; i++)
			cout << list[i];
		cout << endl;
	}
	else {
		for(int i = low; i <= high; i++) {
			swap(list[i], list[low]);
			perm(list, low+1, high);
			swap(list[i], list[low]);
		}
	}
}

BFS

类似于树的按层次遍历的过程,一般用来求最短路径最小操作等等

一般步骤

  • 把根结点放到队列的末尾
  • 每次从队列的头部取出一个元素,查看这个元素所有的下一级元素,把它们放到队列的末尾。并把这个元素标记为它下一级元素的前驱
  • 找到所要找的元素时结束程序
  • 如果遍历整个树都还没有找到,则结束程序
void bfs(起始点) {
	将起始点放入队列中;
	标记起点访问;
	while(如果队列不为空){
		访问队列队首元素x;
		删除队首元素;
		if(出现最终状态) {
			做相应处理
		}
		for(遍历所有x相邻点){
			if(该点未被访问过且合法){
				将该点加入队列末尾;
				做相应操作:记录距离,更新前置结点等
			}
		}
	}
	队列为空,广搜结束;
}

经典问题(走迷宫)

描述
给定一个 N ∗ M N*M NM的迷宫,每一步可以向上下左右四个方向走动,求出从起点到终点所需的最小步数
(’#’,’.’,‘S’,'G’分别表示墙壁,通道,起点和终点)

样例输入

#S######.#
......#..#
.#.##.##.#
.#........
##.##.####
....#....#
.#######.#
....#.....
.####.###.
....#...G#

样例输出

22
#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
const int INF = 100000; 
const int MAX = 101;
typedef pair<int, int> P;
char map[MAX][MAX];
int d[MAX][MAX];//表示起点到各个位置的最短距离 
int sx, sy, gx, gy;//表示起点和终点坐标 
int n, m;
int dx[4] = {1, 0, -1, 0};
int dy[4] = {0, 1, 0, -1};

bool Check(int x, int y) {
	if(x>=0 && x<n && y>=0 && y<m && d[x][y]==INF && map[x][y]!='#')
		return true;
	else return false;
} 

int bfs() {
	queue<P> que;
	for(int i = 0; i < n; i++)
		for(int j = 0; j < m; j++)
			d[i][j] = INF;
	
	que.push(P(sx, sy));
	d[sx][sy] = 0;
	
	while(!que.empty()) {
		P p = que.front(); 
		que.pop();
		if(p.first == gx && p.second == gy)
			break;
		for(int i = 0; i < 4; i++) {
			int nx = p.first + dx[i];
			int ny = p.second + dy[i];
			if(Check(nx, ny)) {
				que.push(P(nx, ny));
				d[nx][ny] = d[p.first][p.second] + 1;
			}
		}
	}
	return d[gx][gy]; 
}

int main() {
	cin >> n >> m;
	for(int i = 0; i < n; i++) {
		for(int j = 0; j < m; j++) {
			cin >> map[i][j];
			if(map[i][j] == 'S') {
				sx = i;
				sy = j;
			}
			if(map[i][j] == 'G') {
				gx = i;
				gy = j;
			}
		}
	}
	int res = bfs(); 
	cout<< res <<endl;
    return 0;
}

你可能感兴趣的:(搜索)