第二章 搜索 No.1BFS之Flood Fill与最短路模型

news2024/12/26 2:21:54

文章目录

    • Flood Fill算法
      • 1097. 池塘计数
      • 1098. 城堡问题
      • 1106. 山峰和山谷
    • 最短路模型
      • 1076. 迷宫问题
      • 188. 武士风度的牛
      • 1100. 抓住那头牛

Flood Fill算法

可以在线性时间复杂度内,找到某个点所在的连通块
想象一个矩阵,有洼地和高地,选择一个洼地,向外扩展新的洼地,遇到高地不扩展。可以用bfs实现也能用dfs实现

bfs的题型大概有两种:一种是矩阵中的最短距离,一种是矩阵变换的最小步数
bfs的两个特点:1. 基于迭代,不会爆栈 2. 最短路


1097. 池塘计数

1097. 池塘计数 - AcWing题库
image.png

经典的Flood Fill,对整张图进行bfs即可

#include <iostream>
using namespace std;

typedef pair<int, int> PII;
const int N = 1010, M = N * N;
char g[N][N];
PII q[M];
int n, m;

void bfs(int x, int y)
{
    int hh = 0, tt = -1;
    q[ ++ tt ] = { x, y };
    g[x][y] = '.';
    while (tt >= hh)
    {
        auto t = q[hh ++ ];
        int tx = t.first, ty = t.second;
        for (int i = tx - 1; i <= tx + 1; ++ i )
            for (int j = ty - 1; j <= ty + 1; ++ j )
            {
            	if (i < 0 || i >= n || j < 0 || j >= m) continue;
                if (i == tx && j == ty) continue;
				if (g[i][j] == '.') continue;
				q[ ++ tt ] = { i, j };
				g[i][j] = '.';
            }
    }
}

int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 0; i < n; ++ i ) scanf("%s", g[i]);
    
    int res = 0;
    for (int i = 0; i < n; ++ i )
        for (int j = 0; j < m; ++ j )
            if (g[i][j] == 'W')
            {
                res ++ ;
                bfs(i, j);
            }
            
    printf("%d\n", res);
    return 0;
}

debug:每次bfs时,hh和tt都要重置,所以不能设置为全局变量,并且还要赋初值


1098. 城堡问题

1098. 城堡问题 - AcWing题库
image.png

这题比较特殊,与上题不同障碍物(墙)不是格子,而是格子的边界
这里只要在bfs扩展时,根据输入的数据判断扩展方向的边界上是否有墙,若没有墙再往外扩展即可

#include <iostream>
using namespace std;

typedef pair<int, int> PII;
const int N = 55, M = N * N;
int g[N][N];
PII q[M]; bool st[N][N];
int n, m;
int dx[4] = { 0, -1, 0, 1 }, dy[4] = { -1, 0, 1, 0 };

int bfs(int x, int y)
{
    int hh = 0, tt = -1, sz = 0;
    q[ ++ tt ] = { x, y };
    st[x][y] = true;
    while (tt >= hh )
    {
        sz ++ ;
        auto t = q[hh ++ ];
        int tx = t.first, ty = t.second;
        for (int i = 0; i < 4; ++ i )
        {
            int nx = tx + dx[i], ny = ty + dy[i];
            if (nx < 0 || nx >= n || ny < 0 || ny >= m) continue;
            if (st[nx][ny]) continue;
            if ((g[tx][ty] >> i) & 1) continue; // 有墙
			q[ ++ tt ] = { nx, ny };
			st[nx][ny] = true;
        }
    }
    return sz;
}

int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 0; i < n; ++ i )
        for (int j = 0; j < m; ++ j )
            scanf("%d", &g[i][j]);
            
    int cnt = 0, res = 0;
    for (int i = 0; i < n; ++ i )
        for (int j = 0; j < m; ++ j )
            if (!st[i][j])
            {
                cnt ++ ;
                res = max(res, bfs(i, j));
            }
            
    printf("%d\n%d\n", cnt, res);
    return 0;
}

debug:每个房间的面积等于bfs迭代的次数,所以每次迭代都要sz ++ ,若只在扩展时sz ++ ,那么sz会少1


1106. 山峰和山谷

1106. 山峰和山谷 - AcWing题库
image.png

所有高度相同的格子为一个连通块,bfs时判断连通块的相邻格子与连通块的高度h关系
若存在高于h的格子,higher为true,若存在低于h的格子,lower为true
最后(反向)判断如果higher为false,表示周围格子都比h矮,所以连通块为山峰
如果lower为false,表示走位格子都比h高,那么连通块为山谷
如果两者都为true,说明周围既有高于h的格子又有低于h的格子,那么该连通块既不是山峰又不是山谷
若两者都为false,说明整个矩阵为一个连通块,那么连通块即是山峰又是山谷

#include <iostream>
using namespace std;

const int N = 1010, M = N * N;
typedef pair<int, int> PII;
PII q[M]; bool st[N][N];
int g[N][N];
int n; 

void bfs(int x, int y, bool &higher, bool &lower)
{
    int tt = -1, hh = 0;
    q[ ++ tt ] = { x, y };
    st[x][y] = true;
    while (tt >= hh )
    {
        auto t = q[hh ++ ];
        int tx = t.first, ty = t.second;
        for (int i = tx - 1; i <= tx + 1; ++ i )
            for (int j = ty - 1; j <= ty + 1; ++ j )
	        {
		        if (i < 0 || i >= n || j < 0 || j >= n) continue;
                if (i == tx && j == ty) continue;
				if (g[i][j] > g[tx][ty]) higher = true;
				else if (g[i][j] < g[tx][ty]) lower = true;
				else if (!st[i][j])
				{
					q[ ++ tt ] = { i, j };
					st[i][j] = true;
				}
            }
    }
}

int main()
{
    scanf("%d", &n);
    for (int i = 0; i < n; ++ i )
        for (int j = 0; j < n; ++ j )
            scanf("%d", &g[i][j]);
            
    int t = 0, v  = 0;
    for (int i = 0; i < n; ++ i )
        for (int j = 0; j < n; ++ j )
            if (!st[i][j])
            {
                bool higher = false, lower = false;
                bfs(i, j, higher, lower);
                if (!higher) t ++ ;
                if (!lower) v ++ ;
            }
            
    printf("%d %d\n", t, v);
    return 0;
}

debug:st数组标记已经访问过的格子,并不是标记和当前bfs的连通块的高度相同的格子
所以以下bfs是错误的

    while (tt >= hh )
    {
        auto t = q[hh ++ ];
        int tx = t.first, ty = t.second;
        for (int i = tx - 1; i <= tx + 1; ++ i )
            for (int j = ty - 1; j <= ty + 1; ++ j )
            {
	            if (i < 0 || i >= n || j < 0 || j >= n) continue;
                if (st[i][j]) continue;
                if (i >= 0 && i < n && j >= 0 && j < n)
				if (g[i][j] > g[tx][ty]) higher = true;
				else if (g[i][j] < g[tx][ty]) lower = true;
				else 
				{
					q[ ++ tt ] = { i, j };
					st[i][j] = true;
				}
            }
    }

扩展时,不能只扩展为标记过的点。因为不仅要扩展高度相同的格子,还需要将当前格子与边界进行比较,若边界的高度为当前格子不同,而边界格子被标记过,那么就会错过比较


最短路模型

1076. 迷宫问题

1076. 迷宫问题 - AcWing题库
image.png

单源最短路,且权值相同,使用bfs即可
每次扩展时维护last数组,记录点由哪个点扩展而来,最后dfs输出last即可

#include <iostream>
using namespace std;

typedef pair<int, int> PII;
const int N = 1010, M = N * N;
int g[N][N];
PII last[N][N], q[M];
int n; bool st[N][N];
int dx[4] = { 0, 0, 1, -1 }, dy[4] = { 1, -1, 0, 0 };

void bfs(int x, int y)
{
    int tt = -1, hh = 0;
    st[x][y] = true;
    q[ ++ tt ] = { x, y };
    while (tt >= hh)
    {
        auto t = q[hh ++ ];
        int tx = t.first, ty = t.second;
        for (int i = 0; i < 4; ++ i )
        {
            int nx = tx + dx[i], ny = ty + dy[i];
            if (nx < 0 || nx >= n || ny < 0 || ny >= n) continue;
            if (g[nx][ny] == 1 || st[nx][ny]) continue;
        
            q[ ++ tt ] = { nx, ny };
            st[nx][ny] = true;
            last[nx][ny] = t;
        }
    }
}

void dfs(int x, int y)
{
    if (x == 0 && y == 0)
    {
        printf("%d %d\n", x, y);
        return;
    }
    dfs(last[x][y].first, last[x][y].second);
    printf("%d %d\n", x, y);
}

int main()
{
    scanf("%d", &n);
    for (int i = 0; i < n; ++ i )
        for (int j = 0; j < n; ++ j )
            scanf("%d", &g[i][j]);
   
    bfs(0, 0);
                
    dfs(n - 1, n - 1);
    return 0;
}

除了后序遍历路径,递归输出,还可以反向求路径:求终点到起点的最短路径,保存每个点的上一个点,最后正序输出

#include <iostream>
using namespace std;

typedef pair<int, int> PII;
const int N = 1010, M = N * N;
int g[N][N];
PII last[N][N], q[M];
int n; bool st[N][N];
int dx[4] = { 0, 0, 1, -1 }, dy[4] = { 1, -1, 0, 0 };

void bfs(int x, int y)
{
    int tt = -1, hh = 0;
    st[x][y] = true;
    q[ ++ tt ] = { x, y };
    while (tt >= hh)
    {
        auto t = q[hh ++ ];
        int tx = t.first, ty = t.second;
        for (int i = 0; i < 4; ++ i )
        {
            int nx = tx + dx[i], ny = ty + dy[i];
            if (nx < 0 || nx >= n || ny < 0 || ny >= n) continue;
            if (g[nx][ny] == 1 || st[nx][ny]) continue;
        
            q[ ++ tt ] = { nx, ny };
            st[nx][ny] = true;
            last[nx][ny] = t;
        }
    }
}

int main()
{
    scanf("%d", &n);
    for (int i = 0; i < n; ++ i )
        for (int j = 0; j < n; ++ j )
            scanf("%d", &g[i][j]);
   
    bfs(n - 1, n - 1);
        
    PII t = { 0, 0 };        
    while (true)
    {
        printf("%d %d\n", t.first, t.second);
        if (t.first == n - 1 && t.second == n - 1) break;
        t = last[t.first][t.second];
    }
    return 0;
}

188. 武士风度的牛

188. 武士风度的牛 - AcWing题库
image.png

一般bfs的扩展都是4个或8个方向,虽然这题的扩展是8方向,但是扩展的位置与平时不同,所以需要特别小心地处理坐标

#include <iostream>
using namespace std;

typedef pair<int, int> PII;
const int N = 155, M = N * N;
int n, m;
char g[N][N];
PII q[M]; bool st[N][N];
int dis[N][N];
int dx[8] = { -2, -2, -1, 1, 2, 2, 1, -1 }, dy[8] = { -1, 1, 2, 2, 1, -1, -2, -2 };

int bfs(int x, int y)
{
    int tt = -1, hh = 0;
    q[ ++ tt ] = { x, y };
    st[x][y] = true;
    while (tt >= hh)
    {
        auto t = q[hh ++ ];
        int tx = t.first, ty = t.second;
        for (int i = 0; i < 8; ++ i )
        {
            int nx = tx + dx[i], ny = ty + dy[i];
            if (nx < 0 || nx >= n || ny < 0 || ny >= m) continue;
            if (st[nx][ny] || g[nx][ny] == '*') continue;
            dis[nx][ny] = dis[tx][ty] + 1;
            if (g[nx][ny] == 'H') return dis[nx][ny];
            q[ ++ tt ] = { nx, ny };
            st[nx][ny] = true;
        }
    }
    return 0;
}

int main()
{
    scanf("%d%d", &m, &n);
    for (int i = 0; i < n; ++ i )
        scanf("%s", g[i]);
        
    for (int i = 0; i < n; ++ i )
        for (int j = 0; j < m; ++ j )
            if (g[i][j] == 'K')
            {
                 printf("%d\n", bfs(i, j));
                 break;
            }
    
    return 0;
}

debug:艹,输入的n和m是反着的,先m再n


1100. 抓住那头牛

1100. 抓住那头牛 - AcWing题库
image.png

将不同的移动方式看成递达不同点的一条边,用bfs从源点向外扩展,第一次递达终点的距离一定是最短的

#include <iostream>
using namespace std;

const int N = 1e5 + 10;
bool st[N];
int q[N], hh, tt = -1;
int dis[N];
int n, k;

int bfs()
{
    q[ ++ tt ] = n;
    st[n] = true;
    while (tt >= hh )
    {
        int t = q[hh ++ ];
        if (t == k) return dis[k];
        if (!st[t - 1] && t - 1 >= 0 && t < N)
        {
            st[t - 1] = true;
            dis[t - 1] = dis[t] + 1;
            q[ ++ tt ] = t - 1;
        }
        if (!st[t + 1] && t + 1 >= 0 && t + 1 < N)
        {
            st[t + 1] = true;
            dis[t + 1] = dis[t] + 1;
            q[ ++ tt ] = t + 1;
        }
        if (!st[t * 2] && t * 2 >= 0 && t * 2 < N)
        {
            st[t * 2] = true;
            dis[t * 2] = dis[t] + 1;
            q[ ++ tt ]= t * 2;
        }
    }
    return -1;
}

int main()
{
    scanf("%d%d", &n, &k);
    printf("%d\n", bfs());
    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/887115.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

经验分享:企业数据仓库建设方案总结!

导读 在企业的数字化转型浪潮中&#xff0c;数据被誉为“新时代的石油”&#xff0c;而数据仓库作为数据管理与分析的核心基础设施&#xff0c;在企业的信息化建设中扮演着重要的角色。本文将深入探讨企业数据仓库建设过程中所遇到的问题以及解决经验&#xff0c;为正在筹备或…

微信程序 自定义遮罩层遮不住底部tabbar解决

一、先上效果 二 方法 1、自定义底部tabbar 实现&#xff1a; https://developers.weixin.qq.com/miniprogram/dev/framework/ability/custom-tabbar.html 官网去抄 简单写下&#xff1a;在代码根目录下添加入口文件 除了js 文件的list 需要调整 其他原封不动 代码&#xf…

电脑键盘打不了字按哪个键恢复?最新分享!

“有没有朋友知道电脑键盘为什么会莫名其妙就打不了字&#xff1f;明明用得好好的&#xff0c;突然就打不了字了&#xff0c;真的让人很迷惑&#xff01;有什么方法可以解决吗&#xff1f;” 电脑键盘为我们的办公提供了很大的方便&#xff0c;我们可以利用键盘输入我们需要的文…

如何用树莓派Pico针对IoT编程?

目录 一、Raspberry Pi Pico 系列和功能 二、Raspberry Pi Pico 的替代方案 三、对 Raspberry Pi Pico 进行编程 硬件 软件 第 1 步&#xff1a;连接计算机 第 2 步&#xff1a;在 Pico 上安装 MicroPython 第 3 步&#xff1a;为 Thonny 设置解释器 第 4 步&#xff…

深度学习实战48-【未来的专家团队】基于AutoCompany模型的自动化企业概念设计与设想

大家好,我是微学AI,今天给大家介绍一下深度学习实战48-【未来的专家团队】基于AutoCompany模型的自动化企业概念设计与设想,文本将介绍AutoCompany模型的概念设计,涵盖了AI智能公司的各个角色,并结合了GPT-4接口来实现各个角色的功能,设置中央控制器,公司运作过程会生成…

GitKraken保姆级图文使用指南

前言 写这篇文章的原因是组内的产品和美术同学&#xff0c;开始参与到git工作流中&#xff0c;但是网上又没有找到一个比较详细的使用教程&#xff0c;所以干脆就自己写了一个[doge]。文章的内容比较基础&#xff0c;介绍了Git内的一些基础概念和基本操作&#xff0c;适合零基…

iOS申请证书(.p12)和描述文件(.mobileprovision)

打包app时&#xff0c;经常会用到ios证书&#xff0c;但很多人都苦于没有苹果电脑&#xff0c;即使有苹果电脑的&#xff0c;也会觉得苹果电脑操作也很麻烦&#xff0c;这里记录一下&#xff0c;用香蕉云编&#xff0c;申请证书及描述文件的过程。 香蕉云编的地址&#xff1a;…

uniapp 上传比较大的视频文件就超时

uni.uploadFile&#xff0c;上传超过10兆左右的文件就报错err&#xff1a;uploadFile:fail timeout&#xff0c;超时 解决&#xff1a; 在manifest.json文件中做超时配置 uni.uploadFile({url: this.action,method: "POST",header: {Authorization: uni.getStorage…

深入理解 Flutter 图片加载原理

作者&#xff1a;京东零售 徐宏伟 来源&#xff1a;京东云开发者社区 前言 随着Flutter稳定版本逐步迭代更新&#xff0c;京东APP内部的Flutter业务也日益增多&#xff0c;Flutter开发为我们提供了高效的开发环境、优秀的跨平台适配、丰富的功能组件及动画、接近原生的交互体验…

wkhtmltopdf 与 .Net Core

wkhtmltopdf 是使用webkit引擎转化为pdf的开源小插件. 其有.NET CORE版本的组件,DinkToPdf,但该控件对跨平台支持有限。 故打算在Linux上安装相关插件直接调用. 准备工作 虚拟机&#xff1a;Linux version 3.10.0-1160.el7.x86_64 wkhtmltox开发包&#xff1a;wkhtmltox_0.12…

【数据结构】链表常见题目-ToDone

文章目录 合并两个有序链表反转链表链表内指定区间反转复制带随机指针的链表环形链表环形链表II相交链表移除链表元素链表中倒数第k个节点链表分割链表的回文结构链表的中间节点旋转链表链表排序链表求和 (逆序求)链表求和II (正序求)重排链表链表的奇偶重排奇偶链表反转链表II…

V2X——行动胜于概念

摘要&#xff1a; 本文从信号灯服务入手&#xff0c;简单介绍了车路协同工程化落地过程中的一些关键问题和解决方案&#xff0c;包括设备接入、V2X消息层编解码、C-V2X通信&#xff0c;车端消费以及车路协同安全防护等&#xff0c;类似的问题还有很多&#xff0c;需要从业者做…

打开vim的语法高亮

在一个Ubuntu中自带的vim版本是8.2.4919&#xff0c;默认就是开始了语法高亮的&#xff0c;打开一个Java文件效果如下&#xff1a; 它不仅仅对Java文件有语法高亮&#xff0c;对很多的文件都有&#xff0c;比如vim的配置文件也有语法高亮&#xff0c;有语法高亮时读起来会容易…

re学习(33)攻防世界-secret-galaxy-300(动态调试)

下载压缩包&#xff1a; 下载链接&#xff1a;https://adworld.xctf.org.cn/challenges/list 参考文章&#xff1a;攻防世界逆向高手题之secret-galaxy-300_沐一 林的博客-CSDN博客 发现这只是三个同一类型文件的三个不同版本而已&#xff0c;一个windows32位exe&#xff0…

Webpack node、output.jsonpFunction 配置详解

Webpack node、output.jsonpFunction 配置详解 最近尝试给一些用到 webpack 的项目升级到最新 webpack5 版本&#xff0c;其中遇到了一些问题&#xff0c;我挑了两个比较典型的问题&#xff0c;其中主要涉及到了 webpack 的 node 属性跟 output.jsonpFunction &#xff08;web…

opencv图像轮廓检测

效果展示&#xff1a; 代码部分&#xff1a; import cv2 import numpy as np img cv2.imread(C:/Users/ibe/Desktop/picture.PNG,cv2.IMREAD_UNCHANGED) # 类型转换 img cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)# 结构元 kernel cv2.getStructuringElement(cv2.MORPH_REC…

【网络架构】华为hw交换机网络高可用网络架构拓扑图以及配置

一、网络拓扑 1.网络架构 核心层:接入网络----路由器 汇聚层:vlan间通信 创建vlan ---什么是vlan:虚拟局域网,在大型平面网络中,为了实现广播控制引入了vlan,可以根据功能或者部门等创建vlan,再把相关的端口加入到vlan.为了实现不用交换机上的相同vlan通信,需要配置中继,为了…

考公-判断推理-逻辑判断-削弱类

否定论点&#xff0c;根本排除 例题 例题 例题 例题 例题 例题 变化小&#xff0c;胖了瘦了 例题 例题 拆桥 例题 例题 例题 例题 例题 例题 例题 例题 例题 A类比非常弱 D削弱了论据 例题 因果倒置例题 例题 例题

css学习2(利用id与class修改元素)

1、id选择器可以为标有特定id的html元素指定特定的样式。 2、选择器以#开头&#xff0c;后跟某id的属性值。 3、class选择器用于描述一组元素的样式&#xff0c;class可以在多个元素使用。 4、类选择器用.选择。 5、指定特定的元素使用class。 6、元素的多个类用空格分开&…

python测试 unittest 实践要点

目录 命名篇 命令行篇 测试断言篇 常见测试断言 特殊测试断言 测试前后篇 在每个测试方法前后执行 在每个测试类的所有方法前后执行 不运行测试篇 参考 命名篇 测试模块应以 test_开头 测试类应以Test开头或结尾 测试类中的测试方法应该以test_开头 命令行篇 测…