bfs:打开转盘锁的最少次数(752)

2023-12-05 21:40
文章标签 最少 次数 bfs 打开 转盘 752

本文主要是介绍bfs:打开转盘锁的最少次数(752),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

在这里插入图片描述

思路:
  这道题目挺有意思的,需要脑经稍微转下,感觉笔试出这种题目的话也是无可厚非。。搜索问题一开始想到dfs,dfs需要构建搜索树啊,怎么构建呢?可以看到一共是4个键,每次可以选择其中一个键上调或者下调,特殊情况是0和9的情况0下调是9,9上调是0.这样每次操作就8个,时间复杂度就是8的n次方,那什么时候结束呢,题目设置了不能走禁区组合,走到就锁住了,如果只是不能触碰禁区没有其他条件如果走不到目标组合就一直走了,==所以我们每次走过的路都存入哈希表。==如果走过,下次就不走了,因为再走就是走老路,这步数肯定不比上次少。直到无路可走,因为都走过了。那就结束
  但是用深度优先搜索的话这个判断机制还是比较难写。因为我们只要走到就行,这种题目适合bfs广度优先。每个节点扩张节点,走过就不扩张,直到走完!
注意到子函数必须写在主函数外面,leetcode官方题目把子函数写在里面了,所以用了不同的方式,我们还是走常规路线把。
但是还是来学习下,这个定义都是auto开头,get是函数名,然后= ,方括号如果虚拟变量要索引就把索引加到方括号,小括号是虚拟变量->给输出的变量类型,这样就定义好了.

 auto get = [&](string& status) -> vector<string

注意到当输出是空的时候是这个形式,前面是function和void

function<void(int, int)> dfs = [&](int index, int steps)

下面附上bfs的代码:

class Solution {
public:char num_prev(char x){return (x == '0' ? '9' : x - 1);}char num_succ(char x) {return (x == '9' ? '0' : x + 1);}// 枚举 status 通过一次旋转得到的数字vector<string> get(string &status){vector<string> ret;for (int i = 0; i < 4; ++i) {char num=status[i];status[i]=num_prev(num);ret.push_back(status);status[i]=num_succ(num);ret.push_back(status);status[i] = num; //千万不能少因为我逐位变化第二位变化的时候第一位不能给他变了啊}return ret;}//进入主函数int openLock(vector<string>& deadends, string target) {unordered_set<string> dead(deadends.begin(), deadends.end()); //这个方式有点特别啊unordered_set<string> seen={"0000"};       //和第一种对比if(target=="0000")return 0;if(dead.count("0000"))return -1;queue<pair<string,int>> q;q.emplace("0000",0);while(!q.empty()){auto [status,step]=q.front();q.pop();for(auto && nextstatus:get(status)){if(!seen.count(nextstatus) && !dead.count(nextstatus)){if(nextstatus==target)return step+1;seen.insert(nextstatus);q.emplace(nextstatus,step+1);}}}return -1;}
};

说到这种搜索式的题目不得不提 滑动谜题,也是用bfs进行搜索,有点像上面的题目。感觉这两道题经常复习,有可能会出现在笔试中。  在这里插入图片描述
思路:
   注意到我们传入的参数step是当前已经完成的step,刚开始传入0,就是完成了0次,然后完成了一次后传入step+1;直接上代码:

class Solution {vector<vector<int>> neighbors = {{1, 3}, {0, 2, 4}, {1, 5}, {0, 4}, {1, 3, 5}, {2, 4}};
public:// 枚举 status 通过一次交换操作得到的状态vector<string> get (string& status) {vector<string> ret;int x = status.find('0');for (int y: neighbors[x]) {swap(status[x], status[y]);ret.push_back(status);swap(status[x], status[y]);}return ret;}  int slidingPuzzle(vector<vector<int>>& board) {string initial;for (int i = 0; i < 2; ++i) {for (int j = 0; j < 3; ++j) {initial += char(board[i][j] + '0');}}if (initial == "123450") {return 0;}queue<pair<string, int>> q;q.emplace(initial, 0);unordered_set<string> seen = {initial};while (!q.empty()) {auto [status, step] = q.front();q.pop();for (auto&& next_status: get(status)) {if (!seen.count(next_status)) {if (next_status == "123450") {return step + 1;}q.emplace(next_status, step + 1);seen.insert(move(next_status));}}}return -1;}
};

同样的,还是一道bfs题目:
在这里插入图片描述
在这里插入图片描述

class Solution {pair<int, int> id2rc(int id, int n) {int r = (id - 1) / n, c = (id - 1) % n;if (r % 2 == 1) {c = n - 1 - c;}return {n - 1 - r, c};}public:int snakesAndLadders(vector<vector<int>> &board) {int n = board.size();vector<int> vis(n * n + 1);queue<pair<int, int>> q;q.emplace(1, 0);while (!q.empty()) {auto p = q.front();q.pop();for (int i = 1; i <= 6; ++i) {int nxt = p.first + i;if (nxt > n * n) { // 超出边界break;}auto rc = id2rc(nxt, n); // 得到下一步的行列if (board[rc.first][rc.second] > 0) { // 存在蛇或梯子nxt = board[rc.first][rc.second]; //蛇或者梯子最终还是让他走后面那条路所以记录后面的。}if (nxt == n * n) { // 到达终点return p.second + 1;}if (!vis[nxt]) {vis[nxt] = true; //这个没问题,先来到这里的,肯定是最短路径,因为后面走的路都一样了!true完事!!!!!!q.emplace(nxt, p.second + 1); // 扩展新状态}}}return -1;}
};

—————————————————————————————————————————————
在这里插入图片描述
在这里插入图片描述
思路:
   这是和广度优先遍历干上了啊,这是和图能联系起来的问题,这种问题是一步步,所以是层序遍历。但是在层序遍历之前需要建立图关系。 有两种方法求解这个题目,官方题解是标准的图法,在B站看到一种比较好理解的思路。都给他记录下吧。上面的图是为了方便理解的。同样颜色的线代表移量公交车能走的区域。
   B站的思路:构建图只是为了方便思路,而不是利用纯粹图知识。首先建造一个哈希表用来存储对应站点邻接的公交车,比如1站点可以有2个公交车来操作,6站点只有一个公交车来操作。
   其次,建立一个关于公交车的set或者在这里是数组,来记录是否访问过某辆公交车。然后建立一个记录站点的队列和bus记录走过的公交车数量。初始值为0
   我们首先将初始站点加入队列,然后进入队列的while,注意到此时bus++,在当前队列中表明上车了。
   如何扩展队列呢?我们通过最开始建造的哈希表查找这个站点对应的公交车,1站点对应1 2两种公交车,我们可以从题目给的条件中获得对应公交车存在的站点。1 2 公交车存在的站点是1273,然后我们把站点扩展到队列中,下次以他们为中心搜索是否没走过的公交车。
   如果公交车走过了,我们就终止这边的扩展,因为同一种的公交车经过的站点我们已经在之前全部加入队列了。在加入的同时我们也判断了站点和目标站点是否相同了。
   注意到每次进入队列我们的bus数量就会+1,为了体现层序遍历,每次都要把队列清空!所以不用担心bus的重复,
   同一次能乘坐的公交车站点如果是走过的公交车他会continue,每个站点的价值在于是否能直到能扩展新的公交车,那么当能扩展新的公交车时,我们会标记公交车走过,然后查找对应公交车的新站点,判断,然后加入队列。表示再次搭了一轮车了。如果队列所有站点都无法扩展新的公交车,此时队列为空,然后返回-1了。

B站题解:

class Solution {
public:int numBusesToDestination(vector<vector<int>>& routes, int source, int target) {if(source==target)return 0;unordered_map<int,vector<int>>m;//某站点对应的公交车for(int i=0;i<routes.size();i++)for(const int stop: routes[i])m[stop].push_back(i);vector<int>ride(routes.size(),0);queue<int>q;q.emplace(source);int buses=0;while(!q.empty()){int size=q.size();buses++;for(int i=0;i<size;i++){   //注意到,前面不要在这里加q.size(),因为每次都是pop了!!int curr=q.front();q.pop();for(const int bus: m[curr]){ //找出站点对应的公交车if(ride[bus]) continue;ride[bus]=1;for(int stop:routes[bus]){//找出公交车对应的站点if(stop==target) return  buses;  q.emplace(stop);  //担心这里会不会重复增加进去呢 的确,重复了,但是没事接下来pop都做过的就会continue}}}}return -1;     }
};

官方题解:

class Solution {
public:int numBusesToDestination(vector<vector<int>>& routes, int source, int target) {if (source == target) {return 0;}int n = routes.size();vector<vector<int>> edge(n, vector<int>(n));unordered_map<int, vector<int>> rec;for (int i = 0; i < n; i++) {for (auto& site : routes[i]) {for (auto& j : rec[site]) {edge[i][j] = edge[j][i] = true; //每个公交车的连通情况 i是初始公交车,j是}rec[site].push_back(i);}}vector<int> dis(n, -1);queue<int> que;for (auto& site : rec[source]) {dis[site] = 1;que.push(site);}while (!que.empty()) {int x = que.front();que.pop();for (int y = 0; y < n; y++) {if (edge[x][y] && dis[y] == -1) {dis[y] = dis[x] + 1;que.push(y);}}}int ret = INT_MAX;for (auto& site : rec[target]) {if (dis[site] != -1) {ret = min(ret, dis[site]);}}return ret == INT_MAX ? -1 : ret;}
};

这篇关于bfs:打开转盘锁的最少次数(752)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/459251

相关文章

如何提高Redis服务器的最大打开文件数限制

《如何提高Redis服务器的最大打开文件数限制》文章讨论了如何提高Redis服务器的最大打开文件数限制,以支持高并发服务,本文给大家介绍的非常详细,感兴趣的朋友跟随小编一起看看吧... 目录如何提高Redis服务器的最大打开文件数限制问题诊断解决步骤1. 修改系统级别的限制2. 为Redis进程特别设置限制

hdu1254(嵌套bfs,两次bfs)

/*第一次做这种题感觉很有压力,思路还是有点混乱,总是wa,改了好多次才ac的思路:把箱子的移动当做第一层bfs,队列节点要用到当前箱子坐标(x,y),走的次数step,当前人的weizhi(man_x,man_y),要判断人能否将箱子推到某点时要嵌套第二层bfs(人的移动);代码如下:

poj 2195 bfs+有流量限制的最小费用流

题意: 给一张n * m(100 * 100)的图,图中” . " 代表空地, “ M ” 代表人, “ H ” 代表家。 现在,要你安排每个人从他所在的地方移动到家里,每移动一格的消耗是1,求最小的消耗。 人可以移动到家的那一格但是不进去。 解析: 先用bfs搞出每个M与每个H的距离。 然后就是网络流的建图过程了,先抽象出源点s和汇点t。 令源点与每个人相连,容量为1,费用为

hdu 3065 AC自动机 匹配串编号以及出现次数

题意: 仍旧是天朝语题。 Input 第一行,一个整数N(1<=N<=1000),表示病毒特征码的个数。 接下来N行,每行表示一个病毒特征码,特征码字符串长度在1—50之间,并且只包含“英文大写字符”。任意两个病毒特征码,不会完全相同。 在这之后一行,表示“万恶之源”网站源码,源码字符串长度在2000000之内。字符串中字符都是ASCII码可见字符(不包括回车)。

POJ 3057 最大二分匹配+bfs + 二分

SampleInput35 5XXDXXX...XD...XX...DXXXXX5 12XXXXXXXXXXXXX..........DX.XXXXXXXXXXX..........XXXXXXXXXXXXX5 5XDXXXX.X.DXX.XXD.X.XXXXDXSampleOutput321impossible

ORACLE 11g 创建数据库时 Enterprise Manager配置失败的解决办法 无法打开OEM的解决办法

在win7 64位系统下安装oracle11g,在使用Database configuration Assistant创建数据库时,在创建到85%的时候报错,错误如下: 解决办法: 在listener.ora中增加对BlueAeri-PC或ip地址的侦听,具体步骤如下: 1.启动Net Manager,在“监听程序”--Listener下添加一个地址,主机名写计

深度优先(DFS)和广度优先(BFS)——算法

深度优先 深度优先搜索算法(英语:Depth-First-Search,DFS)是一种用于遍历或搜索树或图的算法。 沿着树的深度遍历树的节点,尽可能深的搜索树的分支,当节点v的所在边都己被探寻过,搜索将回溯到发现节点v的那条边的起始节点。这一过程一直进行到已发现从源节点可达的所有节点为止。如果还存在未被发现的节点,则选择其中一个作为源节点并重复以上过程,整个进程反复进行直到所有节点都被访

搜狗浏览器打开CSDN博客排版错乱问题解决

之前发生过几次,不知道什么原因。 今天一直用着好好的,打开一个csdn连接,显示404,博文被删除了,于是就用百度快照打开试试,百度快照打开显示的排版很乱也没找到有用信息。 后面再浏览CSDN博客就排版错乱,显示一个大大二维码图片。 尝试删除IE缓存无效,使用谷歌浏览是好的。 基本锁定就是搜狗缓存导致的,于是找如何删除搜狗缓存   清除后恢复正常

C语言练习题之 数组中出现次数超过一半的数

题目描述 给一个长度为 n 的数组,数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。 例如输入一个长度为9的数组[1,2,3,2,2,2,5,4,2]。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。 数据范围:n≤50000,数组中元素的值0≤val≤10000 要求:空间复杂度:O(1),时间复杂度O(n) 输入描述: 保证数组输入非空,且保证有

我成功在本地打开了Cesium啦!

1首先下载Node.js,我是跟着这篇下载的,https://zhuanlan.zhihu.com/p/77594251,不过这后面的我没弄对Cesium环境配置也没影响。 另外:我看其他推文说,在终端写node -v和npm-v查node和npm的版本可以检测node和npm是否下载成功。 2然后我在CesiumB站官号看的教学视频,跟着下载Cesium源代码。 Cesium基础入门1-零