各种搜索汇总

本文详细介绍了搜索算法在解决最短路径和最小步数问题中的应用,包括优先队列BFS、Dijkstra算法以及双端队列在搜索中的作用。举例说明了FLOOD FILL、多源BFS、双向BFS等问题,并探讨了剪枝策略在回溯法中的重要性。同时,文章提到了A* 和迭代加深IDDFS等高级搜索算法,强调了细节在算法解题中的关键作用。
摘要由CSDN通过智能技术生成

开头先发一个暴论其实不是我说的;
其实搜索DP图论题都是一样的,因为都是建立在图的基础上;
DP就是一种特殊的方式解决一类特殊的问题这类问题是有向边构成的,并且是有topo序的,所以可以用DP去推导出来;
比如说f[i,j]中,这个就可以看成是一个节点,并且由f[i-1,j]和f[i-1,j-v[i]]推过来的;所以说DP是是一种思维方式而不是一种算法;
但是这个怎么建模怎么去推就是外话了;
搜索中,最短路就不用说了,最小步数的问题也是可以看成1个状态是一个节点;那么能转移到的状态之间连上一条边;
每个搜索的题目都会产生一棵搜索树,为了让我们搜索的效率更高,对此我们需要各种各样的搜法;
最短路倞问题的搜索树就是原来的图,而最小步数问题的搜索树会更难想一点,每个节点都是一个状态,比如说填数独那道题,你看着最后一个状态的前一步可以由很多种方式转移过来,数独上的每个点都可以少掉;所以有几个格子他就能由几种状态转移过来;
在第一版的博客里面我是没有讲搜索树的,现在补上去;
当然搜索树搜索图都一样,树就是图;
DFS能解决的不只是最小步数和最短路倞,以上两个是一定有搜索树的,
图论就不用说了;

优先队列BFS和Dijkstra

为什么把这两个放在一起讲,因为这两个特别像;
优先队列每次都把当前已经确定的点中选出一个与原点距离最短的出来然后再把这个玩意用来更新所有没有确定的点;
dijkstra也是这样;

这个搜索图就是普通的正权图,不能有负权的说法其实不完全对,比如下面一种情况DIJKSTRA也能算;
在这里插入图片描述
如果你觉得不能算你可以去试一试,反正我是试过了;
为什么说DIJKSTRA不能搜索大部分的负权图,因为他要符合贪心的性质,对吧,都是正权的情况下肯定是行的,如果有负权就不行了,比如下面这种
在这里插入图片描述
如果你觉得行的话你可以去试一试,这就不满足贪心选择性质了;
DIJKSTRA也不能求负环,因为dist是会定下来的,定下来就不去变了
它是看vis数组来进行入队出对操作而不是像SPFA一样通过每次松一下来执行的;当然如果你把堆优化DIJKSTRA改成了优先队列SPFA那么也是可以求的,没改几行;

代码:

朴素算法dijkstra:

#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 510;
int n, m, g[N][N], dist[N];
bool st[N];
int dijkstra()
{
   
    memset(dist, 0x3f, sizeof dist);
    dist[1] = 0;
    for (int i = 0; i < n - 1; i ++ )
    {
   
        int t = -1;
        for (int j = 1; j <= n; j ++ )
            if (!st[j] && (t == -1 || dist[t] > dist[j]))
                t = j;
        for (int j = 1; j <= n; j ++ )
            dist[j] = min(dist[j], dist[t] + g[t][j]);
        st[t] = true;
    }
    if (dist[n] == 0x3f3f3f3f) return -1;
    return dist[n];
}
int main()
{
   
    scanf("%d%d", &n, &m);
    memset(g, 0x3f, sizeof g);
    while (m -- )
    {
   
        int a, b, c;
        scanf("%d%d%d", &a, &b, &c);
        g[a][b] = min(g[a][b], c);
    }
    printf("%d\n", dijkstra());
    return 0;
}

堆优化dijkstra:(之前代码CE的现在已经修正了)

#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
using namespace std;
const int N=100010,M=1000010;
int head[N],ver[M],edge[M],ne[M],d[N];
bool vis[N];
int n,m,tot;
priority_queue < pair<int,int > >q;

void add(int x,int y,int z){
   
    ver[++tot]=y;edge[tot]=z;ne[tot]=head[x];head[x]=tot;
}

void dijkstra() {
   
    memset(d,0x3f,sizeof d);
    memset(vis,0,sizeof vis);
    d[1]=0;
    q.push({
   0,1});
    while(q.size()){
   
        int x = q.top().second; q.pop();
        if(vis[x])continue;
        vis[x]=1 ;
        for(int i=head[x];i;i=ne[i]){
   
            int y=ver[i],z=edge[i];
            if(d[y] > d[x]+z){
   
                d[y] = d[x] + z;
                q.push({
   -d[y],y});
            }
        }
    }
}

int main(){
   
    cin>>n>>m;
    for(int i=1;i<=m;i++){
   
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        add(x,y,z);
    }
    dijkstra();
    for(int i=1;i<=n;i++){
   
        printf("%d\n",d[i]);
    }
    return 0;
}

优先队列的bfs就和这个相似;

双端队列

就是用deque来实现的bfs,因为在边权为01的情况下queue里面的东西满足单调性和两段性的;所以可以从front插入,也可以从back插入;能省很多时间;

但是呢这个搜法比较鸡肋;因为要两段性,所以只能在边权为01的情况下能用;可以看成是priority_queue的特殊情况;

例题

电路维修

这题比较操蛋;难以转化题目给的条件;可以将题目中给的链接方式边权为0;如果遇到要转动的时候边权就为1;0入对头,1入队尾;每次从队头取出元素;这样子就能保证在0没有取完的时候不会取到1,1没有取完的时候不会取到2;
听说用 堆优化过的dijkstra 也能做;因为25W个点,所以必须堆优化一下;优化之后应该会过,数量级是1-2千万级别的;
大算法就是搜索,小细节就是题目条件的转化和每个条件的存储和表示;
因为小细节还是比较重要的所以特地copy了一下y总的代码:

#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
typedef pair<int,int> PII;
const int N=510 , M= N*N;
int n,m,dist[N][N];
char g[N][N];
bool st[N][N];
int bfs(){
   
	memset(dist,0x3f,sizeof dist);
	memset(st,0,sizeof st);
	dist[0][0] = 0;
	deque<PII> q;
	q.push_back({
   0,0}) ;
	char cs[]="\\/\\/";
	int dx[4]={
   -1,-1,1,1}, dy[4]={
   -1,1,1,-1};
	//d数组储存的是从一个点到4个斜向的方向的变化距离;
	int ix[4]={
   -1,-1,0,0}, iy[4]={
   -1,0,0,-1};
	//i数组储存的是从一个点到四周的格子的距离;
	while(q.size()){
   
		PII t=q.front();
		q.pop_front();
		
		if(st[t.first][t.second])continue;
		st[t.first][t.second] = true;
		
		for(int i=0;i<4;i++){
   
			int a=t.first+dx[i] , b=t.second+dy[i];
			if(a<0||a>n||b<0||b>m)continue;//点越界了
			int ca=t.first+ix[i],  cb = t.second+iy[i];//枚举格子
			int d = dist [t.first][t.second]+ (g[ca][cb]!=cs[i]);
			// !=用的很灵性,如果不等于结果为真值为1;
			if (d<dist [a][b]){
   
				dist [a][b] = d;
				if(g[ca][cb]!=cs[i]) q.push_back({
   a,b});
				else q
各大搜索引擎的搜索语法虽然在细节上可能会有所不同,但它们都提供了一种扩展和精确化搜索结果的方式。这里总结了一些常见的搜索引擎语法: 1. **高级搜索**: - Google: 使用 "site:" 来限定搜索范围(如 "site:example.com"),"intitle:" 和 "inurl:" 来筛选特定关键词出现在标题或URL中的页面。 - Bing: 使用 "site:"、"filetype:" (限制文件类型)、"related:" (找到相关的网页)。 - Yahoo: 类似于Google,使用 "site:", "intext:", "intitle:"。 2. **布尔运算符**: - AND, OR, NOT: 连接、组合和排除关键词。例如:"apple AND phone" 会显示包含“苹果”和“手机”的内容,"cat NOT dog" 排除含有“狗”的结果。 3. **引号包围短语**: - 引号将一组词视为一个整体,精确匹配整个短语。如:“人工智能”。 4. **通配符**: - * (星号): Google 的通配符表示零个或多个字符,如 "*google*" 可以找到 "google", "googledrive", 等。 - Bing: 使用 "?" 替换单个字符,"+" 表示必须存在。 5. **日期范围**: - Google: 使用 "since:" 或 "before:" 指定日期范围。 - Bing: 使用 "date:" 关键字后跟日期或年份范围。 6. **排除搜索**: - Google: 在搜索词前加减号 "-",如 "-spam" 会排除包含 "spam" 的结果。 - Bing: 使用 "!keyword"。 7. **位置搜索**: - Google: "near:" 后接地址查找附近的地方。 - Bing: "定位:"。 8. **知识图谱查询**: - Google: 提供长尾信息如 "定义:人工智能" 或 "谁发明了互联网"。 - Bing: 使用 "wiki:" 命令直接获取维基百科的信息。 了解这些基本语法可以帮助你更有效地利用搜索引擎查找所需信息。不同搜索引擎可能还有其他特性和扩展功能,请根据实际需求查阅各个搜索引擎的帮助文档。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值