【优选算法专栏】专题十六:BFS解决最短路问题(二)

简介: 【优选算法专栏】专题十六:BFS解决最短路问题(二)


最小基因变化

题目来源:Leetcode433.最小基因变化

基因序列可以表示为一条由 8 个字符组成的字符串,其中每个字符都是 ‘A’、‘C’、‘G’ 和 ‘T’ 之一。

假设我们需要调查从基因序列 start 变为 end 所发生的基因变化。一次基因变化就意味着这个基因序列中的一个字符发生了变化。

例如,“AACCGGTT” --> “AACCGGTA” 就是一次基因变化。

另有一个基因库 bank 记录了所有有效的基因变化,只有基因库中的基因才是有效的基因序列。(变化后的基因必须位于基因库 bank 中)

给你两个基因序列 start 和 end ,以及一个基因库 bank ,请你找出并返回能够使 start 变化为 end 所需的最少变化次数。如果无法完成此基因变化,返回 -1 。

注意:起始基因序列 start 默认是有效的,但是它并不一定会出现在基因库中。

算法原理

本题目首先可以进行转化:

->转化为图论问题中的边权为1的最短路问题。

根据基因变换只能变换一个,我们把每一个基因抽象成一个点,那么这个问题就可以转化为:已知起点位置和终点位置,从起点位置到终止位置的最短路径,由于每次只能变换一个字母,所以边权都为1。

分析到这,解题思路就是用BFS。

但是在写代码前,还需注意一下细节:

  1. 为防止有的遍历重复,我们需要用一个数组来进行标记,但是普通数组显然不能满足需求,因为存的是字符串,所以我们应用哈希表进行储存。
  2. 如何枚举出所有的情况呢?
    答案很简单,我们暴力枚举一下字符串即可。
  3. 枚举出的所以情况,都需要加入到队列里么?
    答案是不需要,因为本身存在一个基因库,在枚举字符串中,在基因库中我们才进入队列。
  4. 如何快速判断在基因库中存在?
    普通做法就是直接遍历整个基因库,但是这样还是显得太麻烦,我们可以先预处理一下,直接将基因库丢在哈希中,在哈希表中进行查找,方便快速加快效率。

代码实现:

class Solution 
{
public:
    int minMutation(string startGene, string endGene, vector<string>& bank) 
    {
        //用来标记是否是已经搜索过的状态
        unordered_set<string> vis;
        unordered_set<string> hash(bank.begin(),bank.end());//存储基因库中里面的字符串
        string change="ACGT";
        //处理边界情况
        if(startGene==endGene) return 0;
        int ret=0;
        queue<string> q;
        q.push(startGene);
        vis.insert(startGene);
        while(q.size())
        {
            ret++;
            int sz=q.size();
            while(sz--)
            {
                string t=q.front();
                q.pop();
                for(int i=0;i<8;i++)
                {
                    //细节问题
                    string tmp=t;
                    for(int j=0;j<4;j++)
                    {
                        tmp[i]=change[j];
                        if(hash.count(tmp)&&!vis.count(tmp))
                        {
                            if(tmp==endGene)
                            return ret;
                            q.push(tmp);
                            vis.insert(tmp);
                        }
                    }
                }
            }
        }
        return -1;
    }
};

单词接龙

题目来源:108.单词接龙

在字典(单词列表) wordList 中,从单词 beginWord 和 endWord 的 转换序列 是一个按下述规格形成的序列:

序列中第一个单词是 beginWord 。

序列中最后一个单词是 endWord 。

每次转换只能改变一个字母。

转换过程中的中间单词必须是字典 wordList 中的单词。

给定两个长度相同但内容不同的单词 beginWord 和 endWord 和一个字典 wordList ,找到从 beginWord 到 endWord 的 最短转换序列 中的 单词数目 。如果不存在这样的转换序列,返回 0。

算法原理:

分析一遍,其实本题跟最小基因库基本是一样的,只是本题的问法不一样:

本题问的是个数,所以我们要再返回层数的基础上加1.

代码实现:

class Solution 
{
public:
    int ladderLength(string beginWord, string endWord, vector<string>& wordList) 
    {
        //存储单词列表
        unordered_set<string> hash(wordList.begin(),wordList.end());
        unordered_set<string> vis;//标记已经搜索过的单词
        //判断边界情况
        if(!hash.count(endWord)) return 0;
        queue<string> q;
        q.push(beginWord);
        vis.insert(beginWord);
        //因为返回的是个数,所以初始化可以初始化为1
        int ret=1;
        while(q.size())
        {
            ret++;
            int sz=q.size();
            while(sz--)
            {
                string t=q.front();
                q.pop();
                for(int i=0;i<t.size();i++)
                {
                    string tmp=t;
                    for(char ch='a';ch<='z';ch++)
                    {
                        tmp[i]=ch;
                        if(!vis.count(tmp)&&hash.count(tmp))
                        {
                            if(tmp==endWord) return ret;
                            q.push(tmp);
                            vis.insert(tmp);
                        }
                    }
                }
            }
        }
        return 0;
    }
};

为高尔夫比赛砍树

题目来源:Leetcode675.为高尔夫比赛砍树

你被请来给一个要举办高尔夫比赛的树林砍树。树林由一个 m x n 的矩阵表示, 在这个矩阵中:

0 表示障碍,无法触碰

1 表示地面,可以行走

比 1 大的数 表示有树的单元格,可以行走,数值表示树的高度

每一步,你都可以向上、下、左、右四个方向之一移动一个单位,如果你站的地方有一棵树,那么你可以决定是否要砍倒它。

你需要按照树的高度从低向高砍掉所有的树,每砍过一颗树,该单元格的值变为 1(即变为地面)。

你将从 (0, 0) 点开始工作,返回你砍完所有树需要走的最小步数。 如果你无法砍完所有的树,返回 -1 。

可以保证的是,没有两棵树的高度是相同的,并且你至少需要砍倒一棵树。

算法原理:

本题首先也可以转化:

可以看这个例子:

根据砍树由低到高,我们可以把砍树的顺序弄出来:

然后我们让每一段的路径都最小,那么总和肯定也就最小。

那么这个问题就可以转化为我们之前做过的迷宫问题。

而本题是若干个迷宫问题。

还记的什么是迷宫问题吗?

迷宫问题从起始点位置到边界情况下的最短路径,此题是前一个位置到后一个位置的最短路径。

问题解决到这,那么我们又怎么知道砍树顺序呢?

这时就需要用到我们的容器,我们用一个二维数组存储,可以是哈希可以是vector只要能将里面进行排序,那么我们就可以拿来用。

代码解决:

class Solution 
{
    int dx[4]={0,0,1,-1};
    int dy[4]={1,-1,0,0};
    
public:
    int cutOffTree(vector<vector<int>>& f) 
    {
       
        //
        int m = f.size(), n= f[0].size();
        //1.准备工作,找出砍树的顺序
        vector<pair<int,int>> tree;
        for(int i=0;i<m;i++)
        {
            for(int j=0;j<n;j++)
            {
                if(f[i][j]>1)
                    tree.push_back({i,j});
            }
        }
        //排序
        sort(tree.begin(),tree.end(),[&](const pair<int,int>&p1,const pair<int,int>&p2)
        {
            return f[p1.first][p1.second]<f[p2.first][p2.second];
        });
        //2.按照顺序砍树
        int bx=0,by=0;
        int ret=0;
        for(auto& [a,b]:tree)
        {
            int step=bfs(f,bx,by,a,b);
            if(step==-1)
            return -1;
            ret+=step;
            bx=a,by=b;
        }
        return ret;
    }
     //3.bfs
    bool vis[51][51];
    int bfs(vector<vector<int>>& f,int bx,int by,int ex,int ey)
    {
            int m = f.size(), n= f[0].size();
            if(bx==ex&&by==ey) return 0;
            queue<pair<int,int>> q;
            //清空之前数据
            memset(vis,0,sizeof vis);
            q.push({bx,by});
            vis[bx][by]=true;
            int step=0;
            while(q.size())
            {
               
                step++;
                int sz=q.size();
                while(sz--)
                {
                    auto [a,b]=q.front();
                    q.pop();
                    for(int i=0;i<4;i++)
                    {
                        int x=a+dx[i],y=b+dy[i];
                        if(x>=0 && x < m  &&y>=0 && y<n&&f[x][y]&&!vis[x][y])
                        {
                            if(x==ex&&y==ey)
                            return step;
                            q.push({x,y});
                            vis[x][y]=true;
                            
                        }
                    }
                }
                
            }
        return -1;
    }
    
};
目录
打赏
0
0
1
0
8
分享
相关文章
|
5月前
|
算法系列之搜索算法-广度优先搜索BFS
广度优先搜索(BFS)是一种非常强大的算法,特别适用于解决最短路径、层次遍历和连通性问题。在面试中,掌握BFS的基本实现和应用场景,能够帮助你高效解决许多与图或树相关的问题。
224 1
算法系列之搜索算法-广度优先搜索BFS
第十五届蓝桥杯pb组国赛E题[马与象] (15分)BFS算法 详解
第十五届蓝桥杯pb组国赛E题[马与象] (15分)BFS算法 详解
245 3
数据结构与算法学习十六:树的知识、二叉树、二叉树的遍历(前序、中序、后序、层次)、二叉树的查找(前序、中序、后序、层次)、二叉树的删除
这篇文章主要介绍了树和二叉树的基础知识,包括树的存储方式、二叉树的定义、遍历方法(前序、中序、后序、层次遍历),以及二叉树的查找和删除操作。
117 0
|
11月前
|
BFS算法的实现
BFS算法的实现
116 1
基于WOA鲸鱼优化的BiLSTM双向长短期记忆网络序列预测算法matlab仿真,对比BiLSTM和LSTM
本项目基于MATLAB 2022a/2024b实现,采用WOA优化的BiLSTM算法进行序列预测。核心代码包含完整中文注释与操作视频,展示从参数优化到模型训练、预测的全流程。BiLSTM通过前向与后向LSTM结合,有效捕捉序列前后文信息,解决传统RNN梯度消失问题。WOA优化超参数(如学习率、隐藏层神经元数),提升模型性能,避免局部最优解。附有运行效果图预览,最终输出预测值与实际值对比,RMSE评估精度。适合研究时序数据分析与深度学习优化的开发者参考。
基于GA遗传优化的BiLSTM双向长短期记忆网络序列预测算法matlab仿真,对比BiLSTM和LSTM
本内容包含基于BiLSTM与遗传算法(GA)的算法介绍及实现。算法通过MATLAB2022a/2024b运行,核心为优化BiLSTM超参数(如学习率、神经元数量),提升预测性能。LSTM解决传统RNN梯度问题,捕捉长期依赖;BiLSTM双向处理序列,融合前文后文信息,适合全局信息任务。附完整代码(含注释)、操作视频及无水印运行效果预览,适用于股票预测等场景,精度优于单向LSTM。
基于PSO粒子群优化的BiLSTM双向长短期记忆网络序列预测算法matlab仿真,对比BiLSTM和LSTM
本项目基于MATLAB2022a/2024b开发,结合粒子群优化(PSO)算法与双向长短期记忆网络(BiLSTM),用于优化序列预测任务中的模型参数。核心代码包含详细中文注释及操作视频,涵盖遗传算法优化过程、BiLSTM网络构建、训练及预测分析。通过PSO优化BiLSTM的超参数(如学习率、隐藏层神经元数等),显著提升模型捕捉长期依赖关系和上下文信息的能力,适用于气象、交通流量等场景。附有运行效果图预览,展示适应度值、RMSE变化及预测结果对比,验证方法有效性。
基于遗传算法的256QAM星座图的最优概率整形matlab仿真,对比优化前后整形星座图和误码率
本内容展示了基于GA(遗传算法)优化的256QAM概率星座整形(PCS)技术的研究与实现。通过Matlab仿真,分析了优化前后星座图和误码率(BER)的变化。256QAM采用非均匀概率分布(Maxwell-Boltzman分布)降低外圈星座点出现频率,减小平均功率并增加最小欧氏距离,从而提升传输性能。GA算法以BER为适应度函数,搜索最优整形参数v,显著降低误码率。核心程序实现了GA优化过程,包括种群初始化、选择、交叉、变异等步骤,并绘制了优化曲线。此研究有助于提高频谱效率和传输灵活性,适用于不同信道环境。
41 10

热门文章

最新文章

AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等

登录插画

登录以查看您的控制台资源

管理云资源
状态一览
快捷访问