Studying-代码随想录训练营day54| 110.字符串接龙、105.有向图的完全可达性、106.岛屿的周长

news2024/11/20 23:21:14

第53天,图论04,加强广搜和深搜的理解练习💪(ง •_•)ง,编程语言:C++

目录

110.字符串接龙

105.有向图的完全可达性

106.岛屿的周长

总结


110.字符串接龙

文档讲解:手撕字符串接龙

题目:110. 字符串接龙 (kamacoder.com)

学习:从示例中可以看出,实际上abc到def的路径不止一条,但是输出的是最短的那条路径。

因此本题主要需要解决的是两个问题:1.图中的线是如何使连在一起(图是如何构成的)2.找到最短的路径。

1.图中的线是如何连在一起:针对一个问题,首先题目中给的输入是没有包含点与点之间的连线的,图中的连线需要我们自己去连,而相连的条件也很直观,即点与点之间字符只差一个。因此我们只需要判断点与点之间的关系,如果只差一个字符,就说明是有连线的。

2.找到最短路径:这里无向图求最短路,广搜最为合适,广搜只要搜到了终点,那么一定是最短的路径。因为广搜就是以起点中心向四周扩散的搜索。本题如果用深搜,会比较麻烦,要在到达终点的不同路径中选则一条最短路。 而广搜只要达到终点,一定是最短路。

注意:本题是一个无向图,需要用标记位,标记着节点是否走过,否则就会死循环!可以使用set来检查字符串是否出现在字符串集合里更快一些。

代码:

#include <iostream>
#include <vector>
#include <string>
#include <unordered_set>
#include <unordered_map>
#include <queue>
using namespace std;

int main() {
    int n; 
    cin >> n;
    string beginStr, endStr;
    cin >> beginStr >> endStr;
    
    unordered_set<string> strSet; //保存字典中的点(使用哈希表,便于查找,查找时间复杂度为O(1))
    string str;
    for (int i = 0; i < n; i++) {
        cin >> str;
        strSet.insert(str);
    }
    
    unordered_map<string, int>visitMap; //记录字符串和路径长度
    queue<string> que; //初始化队列
    que.push(beginStr);
    visitMap.insert(pair<string, int>(beginStr, 1)); //初始化visitMap
    
    while(!que.empty()) {
        string word = que.front();
        que.pop();
        int path = visitMap[word]; //这个字符串在路径中的长度

        // 开始在这个str中,挨个字符去替换
        for (int i = 0; i < word.size(); i++) {
            string newWord = word; //用一个新字符串替换str,因为每次要置换一个字符

            for (int j = 0 ; j < 26; j++) { //遍历26个字母
                newWord[i] = j + 'a';
                if (newWord == endStr) { //处理特殊情况,如果发现替换字母后,字符串与终点字符相同
                    cout <<  path + 1 << endl; // 找到了路径 
                    return 0;
                }
                //字符串集合里出现了newWord,并且newWord没有被访问过
                if (strSet.find(newWord) != strSet.end() //如果发现改完后的字符在字典中
                        && visitMap.find(newWord) == visitMap.end()) {
                    //添加访问信息,并将新字符串放到队列中
                    visitMap.insert(pair<string, int>(newWord, path + 1));
                    que.push(newWord);
                }
            }
        }
    }
    // 没找到输出0
    cout << 0 << endl;
    return 0;
}

105.有向图的完全可达性

文档讲解:手撕有向图的完全可达性

题目:105. 有向图的完全可达性 (kamacoder.com)

学习:本题定义的是一个有向图,本质上是一个有向图搜搜全路径的过程。可以采用深搜dfs 或者广搜 bfs来进行搜索。

1.采用深搜dfs:我们可以从深搜三部曲出发进行分析。

确定返回值和参数列表:首先我们需要传入用以遍历,接着我们还需要知道当前的节点key,最后我们还需要一个visited数组,来确定哪些点已经被遍历了,防止走回头路。

void dfs(const vector<list<int>>& graph, int key, vector<bool>& visited) {

确定终止条件:我们再遍历的过程中,从一个节点到另一个节点,是依据当前节点的所有出度进行的。而为了防止出现走回头路或者陷入死循环的情况(同时本题也规定了是从节点1出发到所有节点)我们用visited数组来记录访问过的节点,该节点默认数组里的元素都是false,一旦遍历到就标记为true。因此当前访问的节点如果是true,说明该节点已经访问过了,那就终止本层递归,如果不是,我们就进入递归。该终止条件可以写在进入递归后,也可以写在进入递归前。

// 写法一:处理当前访问的节点
void dfs(const vector<list<int>>& graph, int key, vector<bool>& visited) {
    if (visited[key]) {
        return;
    }
    visited[key] = true;
    list<int> keys = graph[key];
    for (int key : keys) {
        // 深度优先搜索遍历
        dfs(graph, key, visited);
    }
}

// 写法二:处理下一个要访问的节点
void dfs(const vector<list<int>>& graph, int key, vector<bool>& visited) {
    list<int> keys = graph[key];
    for (int key : keys) {
        if (visited[key] == false) { // 确认下一个是没访问过的节点
            visited[key] = true;
            dfs(graph, key, visited);
        }
    }

 处理目前搜索节点出发的路径:本题的处理逻辑实际上很简单,就是遍历到节点就把其标记为true,没有则进入新的循环,直到把所有的true都标记完。本题也没有回溯操作,因为我们不需要保存路径只需要标记即可。

代码:最后我们可以得到代码(dfs)

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

//1.确定返回值和参数列表
void dfs(const vector<list<int>>& graph, int key, vector<bool>& visited) { //什么时候能加const取决于,graph是否需要改变
    list<int> keys = graph[key]; //邻接表方式
    for (int key : keys) {
        //2.确定终止条件:将终止条件写在进入递归前,如果不符合直接不进入递归
        if (visited[key] == false) { //找到下一个没有访问过的点
            visited[key] = true;
            dfs(graph, key, visited);
        }
    }
}

int main() {
    int n, m;
    cin >> n >> m;
    
    int s,t;
    vector<list<int>> graph(n + 1); //节点编号从1到n,所以申请 n+1 这么大的数组(采用邻接表的方式)
    while (m--) {
        cin >> s >> t;
        graph[s].push_back(t);
    }
    vector<bool> visited(n + 1, false);
    visited[1] = true; // 节点1 预先处理
    dfs(graph, 1, visited);
    //检查是否都访问到了
    for (int i = 1; i <= n; i++) {
        if (visited[i] == false) { //如果存在没有访问过的点,则说明有向图不具备完全可达性
            cout << -1 << endl;
            return 0;
        }
    }
    cout << 1 << endl;
}

代码:采用bfs的方法,我们也可以通过一个count进行计数,假如count = n则说明每个点都遍历到了。

#include <iostream>
#include <vector>
#include <list>
#include <queue>
using namespace std;

int main() {
    int n, m;
    cin >> n >> m;
    
    int s, t;
    vector<list<int>> graph(n + 1); //保存地图,同时节点编号是从1开始的,因此开拓n+1的空间
    while (m--) {
        cin >> s >> t;
        graph[s].push_back(t);
    }
    vector<bool> visited(n + 1, false); //访问数组
    visited[1] = true; //初始化visited数组
    queue<int> que; //设置队列准备开始遍历
    que.push(1); //初始化队列

    // BFS
    int count = 1;
    while (!que.empty()) {
        int key = que.front(); 
        que.pop();
        list<int> keys = graph[key]; //邻接表的方式
        for (int key : keys) {
            if (!visited[key]) { //如果这个点没有被访问
                que.push(key);
                visited[key] = true;
                count++;
            }
        }
    }
    if(count == n) {
        cout << 1 << endl;
    }
    else {
        cout << -1 << endl;
    }
    return 0;
}

106.岛屿的周长

文档讲解:手撕岛屿的周长

题目:106. 岛屿的周长 (kamacoder.com)

学习:本题是求岛屿的周长,实际上本题不需要进行dfs或者bfs搜索方式,因为本题求周长更多的是一个数学问题。本题的解法有两种

第一种:根据题意,我们可以发现如果一块陆地:边是靠水的,或者是边界,则说明该边是外边,即属于周长计算里面;而边如果是靠陆地的,则说明该边是内边,不包含周长里面的。

因此我们可以通过遍历地图,找到一个陆地之后,就判断其上下左右的状态,如果是外边,则周长+1,如果是内边则周长不变,最后我们就可以求得岛屿的周长。

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

int main() {
    int n, m;
    cin >> n >> m;
    
    vector<vector<int>> grid(n, vector<int>(m, 0)); //保存地图
    for(int i = 0; i < n; i++) {
        for(int j = 0; j < m; j++) {
            cin >> grid[i][j]; 
        }
    }
    int dir[4][2] = {0, 1, 1, 0, -1, 0, 0, -1}; //四个方向
    int result = 0;
    for(int i = 0; i < n; i++) {
        for(int j = 0; j < m; j++) {
            if(grid[i][j] == 1) { //找到了一个土地
                for(int k = 0; k < 4; k++) {
                    int nextx = i + dir[k][0];
                    int nexty = j + dir[k][1];
                    //两种情况,在四条边界处或者是海洋
                    if(nextx < 0 || nexty < 0 || nextx >=n || nexty >= m || grid[nextx][nexty] == 0) {
                        result++;
                    }
                }
            }
        }
    }
    cout << result << endl;
    return 0;
}

第二种:我们可以发现,岛屿的中边长,为岛屿的陆地数量*4,而每有一个陆地和另一个陆地是相邻的,边的数量-2,因此最后岛屿的周长就为:岛屿的陆地数量*4 - cover*2(cover表示相邻的边的数量)

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

int main() {
    int n, m;
    cin >> n >> m;
    
    vector<vector<int>> grid(n, vector<int>(m, 0)); //保存地图
    for(int i = 0; i < n; i++) {
        for(int j = 0; j < m; j++) {
            cin >> grid[i][j]; 
        }
    }
    int sum = 0;
    int cover = 0;
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            if (grid[i][j] == 1) {
                sum++; // 统计总的陆地数量
                //为了避免重复计算,我们只统计每一块陆地上边和左边的相邻情况,这样就能够覆盖所有的情况
                // 统计上边相邻陆地
                if(i - 1 >= 0 && grid[i - 1][j] == 1) cover++;
                // 统计左边相邻陆地
                if(j - 1 >= 0 && grid[i][j - 1] == 1) cover++;
            }
        }
    }
    cout << sum*4 - cover*2 << endl;
    return 0;
}
    

总结

今天的三道题是三种完全不同的解法和思路。

第一道题,告诉了我们图的边没有给我们的时候,是如何进行边的构造的。同时如何采用bfs方式找到最短的路径。

第二道题,是有向图搜索全路径的问题,重点在于dfs和bfs的基础考察,属于图的遍历问题。

第三道题,是让我们不要盲目在图论中使用dfs和bfs,还是要读懂题意,理解题意,才能找到解题的关键。

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

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

相关文章

黑马Java零基础视频教程精华部分_12_面向对象进阶(4)_内部类

《黑马Java零基础视频教程精华部分》系列文章目录 黑马Java零基础视频教程精华部分_1_JDK、JRE、字面量、JAVA运算符 黑马Java零基础视频教程精华部分_2_顺序结构、分支结构、循环结构 黑马Java零基础视频教程精华部分_3_无限循环、跳转控制语句、数组、方法 黑马Java零基础视…

【九】Hadoop3.3.4HA高可用配置

文章目录 1.高可用基本原理1.NameNode 高可用性主备 NameNodeJournalNode 2.Zookeeper 协调3.Quorum Journal Manager (QJM)4.Failover 控制器5.元数据共享6.检查点机制7.切换过程 2.Hadoop高可用配置1.环境背景2.hdfs-site.xml基本配置高可用配置 3.core-site.xml基本配置代理…

【C++】模版:范式编程、函数模板、类模板

目录 一.范式编程 二.函数模板 1.概念与格式 2.原理 3.实例化 4.匹配规则 三.类模板 一.范式编程 在写C函数重载的时候&#xff0c;可能会写很多同一类的函数&#xff0c;例如交换函数&#xff1a; void Swap(int& left, int& right) {int temp left;left r…

推荐 3款电脑上不可或缺的神仙软件,一用就再也离不开

WinForGIFSicle WinForGIFSicle是一款基于GIFSicle的可视化批量GIF压缩工具&#xff0c;具有多种功能和特点。首先&#xff0c;它是一个小巧、免费且开源的软件&#xff0c;能够有效地压缩GIF动画文件。该工具支持按比例压缩和按压缩比压缩两种模式&#xff0c;用户可以根据需要…

MySQL-分库分表

目录 介绍 问题分析 拆分策略 垂直拆分 垂直分库 垂直分表 水平拆分 水平分库 水平分表 实现技术 MyCat 介绍 目录 结构 入门 配置 schema.xml schema标签 datanode标签 datahost标签 rule.xml server.xml system标签 user标签 分片 垂直拆分 水平拆…

【QT】绘图API

目录 绘图API核心类 第一步&#xff1a;重写paintEvent事件函数 第二步&#xff1a;创建QPainter 第三步&#xff1a;设置QPainter绘制的文字类型&#xff08;非必须&#xff09; 第四步&#xff1a;设置画笔属性&#xff08;线条&#xff09; 第五步&#xff1a;设置画…

【复旦微FM33 MCU 外设开发指南】总集篇

各位好&#xff0c;这里是冷凝雨。 本系列旨在为复旦微的MCU提供全面的开发指南 以FM33LC0xx&#xff08;Arm M0&#xff09;为例&#xff0c;与现有的其余MCU型号&#xff0c;如FM33LG0xx、FM33M0xx等有许多相同之处。 本系列将侧重寄存器开发&#xff0c;分享一些各外设使用的…

线上静态的前端、nginx环境基础、基于域名的虚拟主机、基于ip的访问、部署nfs

一、环境基础&#xff1a; 1.查看文件&#xff08;不要注释及空行&#xff09; [root17dns ~]# grep -Ev "#|^$" /usr/local/nginx/conf/nginx.conf 2.备份源文件&#xff1a; [root17dns ~]# cp /usr/local/nginx/conf/nginx.conf /usr/local/nginx/conf/nginx.con…

希尔排序, 插入排序, 冒泡排序, 选择排序【C++】

希尔排序&#xff0c; 插入排序&#xff0c; 冒泡排序&#xff0c; 选择排序 测试代码希尔排序选择排序冒泡排序插入排序 测试代码 #include <iostream> using namespace std;int main() {int arr[6] { 0 };int len sizeof(arr) / sizeof(int);for (int i 0; i < …

混合精度、异构计算——杂记

1、英伟达GPU架构 Figure 1 shows a full GA100 GPU with 128 SMs. The A100 is based on GA100 and has 108 SMs. SM是streaming multiprocessor的简写&#xff0c;4个处理单元组成一个SM&#xff0c;如Figure 2。 每个SM有64个INT32&#xff0c;64个FP32&#xff0c;32个F…

【Linux网络】网络层协议:IP

本篇博客整理了 TCP/IP 分层模型中网络层的 IP 协议&#xff0c;旨在让读者更加深入理解网络协议栈的设计和网络编程。 目录 一、网络层 二、IP 报头 1&#xff09;报头与有效载荷的分离 2&#xff09;有效载荷的上交 3&#xff09;源 IP 与目的 IP 4&#xff09;生存时间…

大模型学习笔记 - 大纲

LLM 大纲 LLM 大纲 1. LLM 模型架构 LLM 技术细节 - 注意力机制LLM 技术细节 - 位置编码 2. LLM 预训练3. LLM 指令微调 LLM 高效微调技术 4. LLM 人类对齐 LLM InstructGPTLLM PPO算法LLM DPO 算法 5. LLM 解码与部署6. LLM 模型LLaMA 系列7. LLM RAG 1. LLM 模型架构 大模…

奥特曼格斗进化0-3汉化版丨奈克瑟斯手机版,PC+安卓合集

今天小孩吵着要玩奈克瑟斯手机游戏&#xff0c;都是平时看那些游戏主播打这些游戏。 看着家里几千张奥特曼卡&#xff0c;脑壳就疼&#xff0c;索性还是闲鱼买了&#xff0c;因为我也没找到很全的。挨个找了个遍就不浪费时间了 百度网盘哈&#xff0c;也懒得转迅雷&#xff0c…

PXE——安装,配置,测试(rhel7环境下)

什么是PXE PXE&#xff08;Preboot eXecution Environment&#xff0c;预启动执行环境&#xff09;允许计算机在开机时从网络而非本地硬盘或其他存储设备启动。这种技术主要用于网络启动和自动化安装系统&#xff0c;尤其在需要为大量计算机同时安装操作系统的情况下非常有用。…

jupyter notebook安装

1.安装 pip install notebook 2.显示配置文件&#xff1a; jupyter notebook --generate-config 3.修改代码路径&#xff1a; 编辑配置文件C:\Users\a\.jupyterjupyter_notebook_config.py 4.运行 jupyter notebook 会自动弹出http://localhost:8888/tree

Elastic:IK分词器分词、停用词热更新如何配置-基于数据库

上一期&#xff0c;我们说明了基于API形式的热更新&#xff0c;但是API形式的热更新存在词库的管理不方便&#xff0c;要直接操作磁盘文件&#xff0c;检索页很麻烦&#xff1b;文件的读写没有专门的优化&#xff0c;性能不好&#xff1b;多一次接口调用和网络传输等缺点&#…

软件测试需要具备的基础知识【功能测试】---前端知识(一)

​ ​ 您好&#xff0c;我是程序员小羊&#xff01; 前言 为了更好的学习软件测试的相关技能&#xff0c;需要具备一定的基础知识。需要学习的基础知识包括&#xff1a; 1、计算机基础 2、前端知识 3、后端知识 4、软件测试理论 后期分四篇文章进行编写&#xff0c;这是第二篇 …

MongoDB未授权访问漏洞

开启MongoDB服务时不添加任何参数时,默认是没有权限验证的,登录的用户可以通过默认端口无需密码对数据库任意操作&#xff08;增、删、改、查高危动作&#xff09;而且可以远程访问数据库。 漏洞原因 造成未授权访问的根本原因就在于启动 Mongodb 的时候未设置 --auth 也很少…

Node.js的下一代浏览器和移动自动化测试框架-WebdriverIO

在现代软件开发中&#xff0c;自动化测试已成为保障软件质量的关键环节。而在众多测试框架中&#xff0c;WebdriverIO凭借其强大的功能和简洁的语法&#xff0c;成为Node.js生态中备受瞩目的浏览器和移动自动化测试框架。那么&#xff0c;WebdriverIO究竟有哪些独特之处&#x…

Substance Painter材质制作原理

21 材质制作原理_哔哩哔哩_bilibili 颜色&#xff0c;纹理&#xff0c;高光 木头的制作 玻璃的制作 玻璃要给一定的金属度