搜索算法
本文最后更新于225 天前,其中的信息可能已经过时,如有错误请发送邮件到2401292661@qq.com

搜索算法

广度优先搜索 BFS

BFS主要求解两种问题:

  1. 最短距离:给出一个起始点,一个终点,并求出在地图上从起点到终点的最短距离
  2. 最小步数:从一种状态变换到另一种状态的最小步数

总的来说BFS求的是最小问题,是基于迭代的,相对于DFS来说不会爆栈,因此在BFS和DFS同时可用时,优先使用BFS。

BFS有一种性质:当所有边的权重都相等时,从起点开始搜索,可以得到所有点的最短距离【因为BFS可能会有多条路径,但每一条路径都同时走一步,所以先到达某点的路径就是最短路径】

Flood Fill算法

顾名思义,Flood Fill算法即洪水填充算法,给出一个起始点,填充附近的点。

Flood Fill算法可以在线性时间复杂度内找到某个点所在的连通块【连通块就是根据连通规则所递归连通的连通块】。

Flood Fill 算法也就是BFS的其中一种算法,给出一个点,然后使用一个队列,将连通点加入队列,然后再一直加入,直到队列空。

八连通与四连通

八连通就是一个点与附近的八个点是连通的,四连通就是一个点与其上、下、左、右四个点是连通的,除了这两种,还有日字连通,就是一个点与可以组成 “日” 字的对角线点是连通的。因为要递归访问连通点,因此需要遍历连通点,以下是遍历方式:

// 表示坐标的方便方法
#define x first
#define y second 
typedef pair<int,int> PII;    

// 八连通 p为点,p.x p.y 分别为p的x坐标与y坐标
for(int i=p.x-1;i<p.x+1;++i){
    for(int j=p.y-1;j<p.y+1;++j){
        PII new_p={i,j};
        //操作
    }
}

// 四连通,因为只有四个点,就可以使用数组存储坐标来进行遍历
int dx[]={0,-1,0,1},dy[]={-1,0,1,0};    //存储相对于p.x的差值
for(int i=0;i<4;++i){
    PII new_p={p.x+dx[i],p.y+dy[j]};
    //操作
}

// 日字连通,与四连通类似方法
int dx[]={-2,-1,1,2,2,1,-1,-2};
int dy[]={1,2,2,1,-1,-2,-2,-1};
for(int i=0;i<8;++i){
    PII new_p={p.x+dx[i],p.y+dy[i]};
    //操作
}

城堡问题

题目描述

    1   2   3   4   5   6   7  
   #############################
 1 #   |   #   |   #   |   |   #
   #####---#####---#---#####---#
 2 #   #   |   #   #   #   #   #
   #---#####---#####---#####---#
 3 #   |   |   #   #   #   #   #
   #---#########---#####---#---#
 4 #   #   |   |   |   |   #   #
   #############################
           (图 1)

   #  = Wall   

   |  = No wall

   -  = No wall
      方向:上北下南左西右东。
      图1是一个城堡的地形图。

请你编写一个程序,计算城堡一共有多少房间,最大的房间有多大。

城堡被分割成 m∗n (m<=50, n<=50)个方格区域,每个方格区域可以有0~4面墙。

注意:墙体厚度忽略不计。

输入格式
第一行包含两个整数 m 和 n,分别表示城堡南北方向的长度和东西方向的长度。

接下来 m 行,每行包含 n 个整数,每个整数都表示平面图对应位置的方块的墙的特征。

每个方块中墙的特征由数字 P 来描述,我们用1表示西墙,2表示北墙,4表示东墙,8表示南墙,P 为该方块包含墙的数字之和。

例如,如果一个方块的 P 为3,则 3 = 1 + 2,该方块包含西墙和北墙。

城堡的内墙被计算两次,方块(1,1)的南墙同时也是方块(2,1)的北墙。

输入的数据保证城堡至少有两个房间。

输出格式
共两行,第一行输出房间总数,第二行输出最大房间的面积(方块数)。

样例
输入样例:

4 7 
11 6 11 6 3 10 6 
7 9 6 13 5 15 5 
1 10 12 7 13 7 5 
13 11 10 8 10 12 13 

输出样例:

5
9
题解

这题就是计算有多少个连通块,并把最大的连通块面积输出。因此,此题使用Flood Fill算法,但难点就是表示墙的方式,使用一个数来代表南、东、北、西,依据题意分别是8、4、2、1,很像二进制的四位数,比如0110=6就是东北有墙,这样的话就可以采用与1,分别将数字右移0、1、2、3位然后与1,这样就可以知道西、北、东、南是否有墙。除此之外,若是先知道某个点的非墙位置然后再入队列,那么这样就会增加代码复杂度,可以先遍历点的四连通点,然后再判断两者之间是否有墙进而入队列。

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

#define x first
#define y second
typedef pair<int,int> PII;
const int N=55,M=N*N;

int m,n;
int g[N][N];    //地图
PII q[M];       //伪队列
bool st[N][N];  //点状态

int bfs(int sx,int sy){
    int dx[]={0,-1,0,1},dy[]={-1,0,1,0};    //注意是数组坐标,dx是竖坐标
    int hh=0,tt=0;  //队首与队尾
    int area=0;
    q[0]={sx,sy};   //加入队列
    st[sx][sy]=true;    //更新状态

    while(hh<=tt){
        PII t=q[hh++];  //队首坐标
        area++;
        for(int i=0;i<4;++i){
            int a=t.x+dx[i],b=t.y+dy[i];
            if(a<0 || a>=m || b<0 || b>=n)continue; //边界条件
            if(st[a][b])continue;   //被访问过
            if(g[t.x][t.y] >> i & 1)continue; //两者之间有墙

            q[++tt]={a,b};  //入队
            st[a][b]=true;  //更新状态
        }
    }
    return area;
}

int main(){
    cin >> m >> n;
    for(int i=0;i<m;++i){
        for(int j=0;j<n;++j){
            cin >> g[i][j];
        }
    }
    int cnt=0,area=0;
    for(int i=0;i<m;++i){
        for(int j=0;j<n;++j){
            if(!st[i][j]){
                //遍历图上所有点,当此点未被遍历过时,bfs
                cnt++;
                area=max(area,bfs(i,j));
            }
        }
    }
    cout << cnt << endl << area;
    return 0;
}

最短路模型

迷宫问题

描述

定义一个二维数组:

int maze[5][5] = {

0, 1, 0, 0, 0,

0, 1, 0, 1, 0,

0, 0, 0, 0, 0,

0, 1, 1, 1, 0,

0, 0, 0, 1, 0,

};

它表示一个迷宫,其中的1表示墙壁,0表示可以走的路,只能横着走或竖着走,不能斜着走,要求编程序找出从左上角到右下角的最短路线。

 

输入

一个5 × 5的二维数组,表示一个迷宫。数据保证有唯一解。

输出

左上角到右下角的最短路径,格式如样例所示。

样例输入

0 1 0 0 0
0 1 0 1 0
0 0 0 0 0
0 1 1 1 0
0 0 0 1 0

样例输出

(0, 0)
(1, 0)
(2, 0)
(2, 1)
(2, 2)
(2, 3)
(2, 4)
(3, 4)
(4, 4)
题解

虽然BFS可以找出最短距离,但却无法记录是哪一条路径,因为BFS的特性,不同路径一定不会交叉只会分叉(因为交叉了说明有条路径提前到达,那么这条路径就可以结束了,因为不可能会比提前到的更快),因此可以使用一个pari类型的二维数组,二维代表原坐标,而值就是走到此点的上一坐标。这样就可以当搜索到终点时,从终点退回输出即可。【可能你会想到根当权重不同时,也可以使用int类型的二维数组记录到达此点距离,当搜索到终点时,就可以找出最短距离,这是不对的,因为当交叉的时候,不知提前到的路径短还是后到的路径短,也许可以先比较,若更短则更新否则不更新】

#include <iostream>
#include <vector>
#include <cstring>
#include <algorithm>
using namespace std;

#define x first
#define y second
typedef pair<int,int> PII;

const int N = 5,M=N*N;

int n=N;
int g[N][N];
PII q[M];
PII pre[N][N];

void bfs(int sx,int sy){
    int dx[]={-1,1,0,0},dy[]={0,0,-1,1};
    int hh=0,tt=0;
    q[0]={sx,sy};

    memset(pre,-1,sizeof pre);
    pre[sx][sy]={0,0};
    while(hh<=tt){
        PII t=q[hh++];
        for(int i=0;i<4;++i){
            int a=t.x+dx[i],b=t.y+dy[i];
            if(a<0 || a>=n || b<0 || b>=n)continue;
            if(g[a][b])continue;
            if(pre[a][b].x!=-1)continue;

            q[++tt]={a,b};
            pre[a][b]=t;
        }
    }
}

int main(){
    for(int i=0;i<n;++i){
        for(int j=0;j<n;++j){
            cin >> g[i][j];
        }
    }
    bfs(4,4);   //从终点到起点,再从起点回退
    PII end={0,0};
    while(true){
        printf("(%d, %d)\n",end.x,end.y);
        if(end.x==n-1 && end.y==n-1)break;
        end=pre[end.x][end.y];
    }
    return 0;
}

抓住那头牛

描述

农夫知道一头牛的位置,想要抓住它。农夫和牛都位于数轴上,农夫起始位于点N(0<=N<=100000),牛位于点K(0<=K<=100000)。农夫有两种移动方式:

1、从X移动到X-1或X+1,每次移动花费一分钟

2、从X移动到2*X,每次移动花费一分钟

假设牛没有意识到农夫的行动,站在原地不动。农夫最少要花多少时间才能抓住牛?

 

输入

两个整数,N和K

输出

一个整数,农夫抓到牛所要花费的最小分钟数

样例输入

5 17

样例输出

4
题解

这题也是BFS,这种类型是有多种方式达到下一点,而走完一步后依旧有那么多的方式继续走,而每一种方式的权重都相同,但所到达的点不同,这样就可以用BFS,因为BFS可以分出多条路径,而每一条路径同时走相同的步数,这样最先到达终点的路径就是最短路径。但这题需要计算花费的时间,虽然每条路径都同时走一步,但不确定到底是哪一步往前走了,所以无法使用一个变量来计算时间。可以使用一个int类型的二维数组记录走到当前所消耗的时间。【因为不可能同时走到同一点,因此不用考虑覆盖问题】

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;

#define x first
#define y second
typedef pair<int,int> PII;

const int N=2e5+10; //注意范围,可能会到两倍的10000

int n,k;
int q[N];
int dist[N];

int bfs(){
    memset(dist,-1,sizeof dist);
    dist[n]=0;
    q[0]=n;
    int hh=0,tt=0;
    while(hh<=tt){
        int t=q[hh++];
        if(t==k) return dist[k];
        if(t+1>=0&&dist[t+1]==-1){
            q[++tt]=t+1;
            dist[t+1]=dist[t]+1;
        }
        if(t-1>=0&&dist[t-1]==-1){
            q[++tt]=t-1;
            dist[t-1]=dist[t]+1;
        }
        if(t*2>=0&&dist[t*2]==-1){
            q[++tt]=t*2;
            dist[t*2]=dist[t]+1;
        }
    }
    return -1;
}

int main(){
    cin >> n >> k;
    cout << bfs() << endl;
    return 0;
}

 

文末附加内容
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇