3

算法总结--搜索 - luokesi

 1 year ago
source link: https://www.cnblogs.com/luokeIT/p/17262242.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

声明(叠甲):鄙人水平有限,本文为作者的学习总结,仅供参考。


1. 搜索介绍

搜索算法包括深度优先搜索(DFS)和广度优先搜索(BFS)这两种,从起点开始,逐渐扩大寻找范围,直到找到需要的答案为止。从时间复杂度来说这与一般的暴力枚举来说没来太大的区别,这样的话我们为什么要使用搜索算法,而不直接使用暴力法呢?首先,搜索算法是暴力法一种优雅的写法,即优雅的暴力,可以为我们的代码减少冗长的嵌套 for 循环。其次搜索通过剪枝操作可以跳过一些无效状态,降低问题规模,从而使效率比直接的枚举所有答案要高。


2. DFS 与 BFS 的区别

类别 DFS BFS
搜索类型 试探搜索 地毯搜索
所用的数据结构 栈(vector也是可以的) 队列
适用的题目 求方案总数 求最短路径
实现方法 一般结合回溯算法一同实现 将可行行方案放入队列,然后一一遍历

3. 举些栗子

3.1 BFS--马的遍历

有一个 n∗m 的棋盘,在某个点 (x,y)(x,y)上有一个马,要求你计算出马到达棋盘上任意一个点最少要走几步。

这是一道经典 BFS 题,可说使模板题了,在解题前先介绍一下 BFS 的实现思路如下:

【1】 构建对应结构体与队列
【2】 初始化数据和初始点
【3】 根据初始点与遍历关系遍历其它符合要求的点
【4】 查询答案

根据 BFS 的实现思路可以容易的得到该题的代码如下

#include <bits/stdc++.h>
#define N_MAX 400
using namespace std;
int mp[N_MAX][N_MAX]; // mp[i][j] 表示马到(i,j)点所需的最少次数
int n,m,x,y;
// 定义 dx dy 便于运算
int dx[] = {-1,1,2,2,1,-1,-2,-2};
int dy[] = {-2,-2,-1,1,2,2,1,-1};
// [1] 定义数据结构体与duilie
struct point{
    int x,y; // 点的坐标
    int t;   // 马到该点的最少次数
};
queue<point> que;

int main()
{
    // [2] 初始化数据
    memset(mp,-1,sizeof(mp));
    cin >> n >> m >> x >> y;
    mp[x][y] = 0; // 初始点为 0

    // [3] 搜索
    que.push((point){x,y,mp[x][y]}); // 先向队列中压入初始点
    while(!que.empty())
    {
        // 从队列中一个一个的遍历
        point p = que.front();
        que.pop(); // 记得弹出
        // 寻找满足条件的点,并压入队列中
        for(int i = 0;i < 8;i++)
        {
            int nx = p.x + dx[i];
            int ny = p.y + dy[i];
            // 判断是否合法
			if(nx >= 1 && ny >= 1 && nx <= n && ny <= m && mp[nx][ny] == -1)
			{
				mp[nx][ny] = p.t + 1;
            	que.push((point){nx,ny,mp[nx][ny]});
			} 	
        }
    }
    // 输出结果
    for(int i = 1;i <= n;i++)
    {
        for(int j = 1;j <= m;j++)
        {    
            cout << mp[i][j] << " ";
        }
        cout << endl;
    }
        
    return 0;
}

3.2 BFS--奇怪的电梯

呵呵,有一天我做了一个梦,梦见了一种很奇怪的电梯。大楼的每一层楼都可以停电梯,而且第 i 层楼(1≤i≤N)上有一个数字 Ki(0≤Ki≤N)。电梯只有四个按钮:开,关,上,下。上下的层数等于当前楼层上的那个数字。当然,如果不能满足要求,相应的按钮就会失灵。例如: 3,3,1,2,5 代表了 Ki(K1=3,K2=3,……),从 1 楼开始。在 1 楼,按“上”可以到 4 楼,按“下”是不起作用的,因为没有 −2 楼。那么,从 A 楼到 B 楼至少要按几次按钮呢?

这题也是一道 BFS 的模板题了,算是用于巩固了,具体 AC 代码如下

#include <bits/stdc++.h>
using namespace std;
#define N_MAX 201
struct point{
	int f;  // 所在层数
	int ki; // 拥有的数字
	int t;  // 需要按的次数 
};
queue<point> que;
int ans[N_MAX];
int n,a,b;
int k[N_MAX];

int main()
{
	memset(ans,-1,sizeof(ans));
	cin >> n >> a >> b;
	for(int i = 1;i <= n;i++)
	{
		cin >> k[i];
	}
	ans[a] = 0;
	// bfs
	que.push((point){a,k[a],ans[a]});
	while(!que.empty())
	{
		point p = que.front();
		que.pop();
		int nf = p.f + p.ki; // 上 
		if(nf <= n && ans[nf] == -1)
		{
			ans[nf] = p.t+1;
			que.push((point){nf,k[nf],ans[nf]});	
		}
		nf = p.f - p.ki;    // 下  
		if(nf >= 1 && ans[nf] == -1)
		{
			ans[nf] = p.t+1;
			que.push((point){nf,k[nf],ans[nf]});	
		}		
	}  
	cout << ans[b] << endl;
	return 0;
}

3.4 DFS--数的组合

给定两个整数 n 和 k,返回范围 [1, n] 中所有可能的 k 个数的组合。你可以按 任何顺序 返回答案。

这是一到典型的 DFS 题,DFS 组要就是利用回溯算法进行解决,回溯的具体思路如下,其难点在于确定递归参数的确定

【1】 写递归出口(收果子)
【2】 循环遍历搜索,并进行剪枝优化
【3】 处理节点
【4】 递归
【5】 回溯,即取消处理节点时的朝左
该题代码如下:

class Solution {
public:
    vector<vector<int>> ret; // 用于存储最后的结果
    vector<int> path;       // 用于存储中间的结果
    
    void bnf(int st,int n,int k)
    {
        // 收果子 (中止条件)
        if(path.size() == k)
        {
            ret.push_back(path);
            return;
        }
        // 循环,并进行剪枝优化
        for(int i = st;i <= n - k + path.size() + 1;++i)
        {
            // 处理节点
            path.push_back(i);
            // 递归
            bnf(i+1,n,k);
            // 回溯
            path.pop_back();
        }
    }
    vector<vector<int>> combine(int n, int k) {
        bnf(1,n,k);
        return ret;
    }
};

代码随想录
洛谷搜索算法推荐题库
马的遍历的洛谷题解
本文到此结束,希望对您有所帮助。

__EOF__


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK