You have a lock in front of you with 4 circular wheels. Each wheel has 10 slots: '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'. The wheels can rotate freely and wrap around: for example we can turn '9' to be '0', or '0' to be '9'. Each move consists of turning one wheel one slot.

The lock initially starts at '0000', a string representing the state of the 4 wheels.

You are given a list of deadends dead ends, meaning if the lock displays any of these codes, the wheels of the lock will stop turning and you will be unable to open it.

Given a target representing the value of the wheels that will unlock the lock, return the minimum total number of turns required to open the lock, or -1 if it is impossible.

Example 1:

  1. Input: deadends = ["0201","0101","0102","1212","2002"], target = "0202"
  2. Output: 6
  3. Explanation:
  4. A sequence of valid moves would be "0000" -> "1000" -> "1100" -> "1200" -> "1201" -> "1202" -> "0202".
  5. Note that a sequence like "0000" -> "0001" -> "0002" -> "0102" -> "0202" would be invalid,
  6. because the wheels of the lock become stuck after the display becomes the dead end "0102".

Example 2:

  1. Input: deadends = ["8888"], target = "0009"
  2. Output: 1
  3. Explanation:
  4. We can turn the last wheel in reverse to move from "0000" -> "0009".

Example 3:

  1. Input: deadends = ["8887","8889","8878","8898","8788","8988","7888","9888"], target = "8888"
  2. Output: -1
  3. Explanation:
  4. We can't reach the target without getting stuck.

Example 4:

  1. Input: deadends = ["0000"], target = "8888"
  2. Output: -1

Note:

  1. The length of deadends will be in the range [1, 500].
  2. target will not be in the list deadends.
  3. Every string in deadends and the string target will be a string of 4 digits from the 10,000 possibilities '0000' to '9999'.

这道题说有一种可滑动的四位数的锁,貌似行李箱上比较常见这种锁。给了我们一个目标值,还有一些死锁的情况,就是说如果到达这些死锁的位置,就不能再动了,相当于迷宫中的障碍物。然后问我们最少多少步可以从初始的0000位置滑动到给定的target位置。如果各位足够老辣的话,应该能发现其实本质就是个迷宫遍历的问题,只不过相邻位置不再是上下左右四个位置,而是四位数字每个都加一减一,总共有八个相邻的位置。遍历迷宫问题中求最短路径要用BFS来做,那么这道题也就是用BFS来解啦,和经典BFS遍历迷宫解法唯一不同的就是找下一个位置的地方,这里我们要遍历四位数字的每一位,然后分别加1减1,我们用j从-1遍历到1,遇到0跳过,也就是实现了加1减1的过程。然后我们要计算要更新位上的数字,为了处理9加1变0,和0减1变9的情况,我们统一给该位数字加上个10,然后再加或减1,最后再对10取余即可,注意字符和整型数之间通过加或减'0'来转换。我们用结果res来记录BFS遍历的层数,如果此时新生成的字符串等于target了,直接返回结果res,否则我们看如果该字符串不在死锁集合里,且之前没有遍历过,那么加入队列queue中,之后将该字符串加入visited集合中即可。注意这里在while循环中,由于要一层一层的往外扩展,一般的做法是会用一个变量len来记录当前的q.size(),博主为了简洁,使用了一个trick,就是从q.size()往0遍历,千万不能反回来,因为在计算的过程中q的大小会变化,如果让k < q.size() 为终止条件,绝b会出错,而我们初始化为q.size()就没事,参见代码如下:

解法一:

  1. class Solution {
  2. public:
  3. int openLock(vector<string>& deadends, string target) {
  4. unordered_set<string> deadlock(deadends.begin(), deadends.end());
  5. if (deadlock.count("")) return -;
  6. int res = ;
  7. unordered_set<string> visited{{""}};
  8. queue<string> q{{""}};
  9. while (!q.empty()) {
  10. ++res;
  11. for (int k = q.size(); k > ; --k) {
  12. auto t = q.front(); q.pop();
  13. for (int i = ; i < t.size(); ++i) {
  14. for (int j = -; j <= ; ++j) {
  15. if (j == ) continue;
  16. string str = t;
  17. str[i] = ((t[i] - '') + + j) % + '';
  18. if (str == target) return res;
  19. if (!visited.count(str) && !deadlock.count(str)) q.push(str);
  20. visited.insert(str);
  21. }
  22. }
  23. }
  24. }
  25. return -;
  26. }
  27. };

下面这种方法也是用的BFS遍历,不同之处在于生成新字符串的方法,这里我们采用拼接法来生成新字符串,而不是像上面那样使用置换字符串的方法。我们对于加一和减一分别进行拼接,注意处理9加1变0,和0减1变9的情况。然后剩下的部分就和经典的BFS遍历写法没有什么太大的区别了,参见代码如下:

解法二:

  1. class Solution {
  2. public:
  3. int openLock(vector<string>& deadends, string target) {
  4. unordered_set<string> deadlock(deadends.begin(), deadends.end());
  5. if (deadlock.count("")) return -;
  6. int res = ;
  7. unordered_set<string> visited{{""}};
  8. queue<string> q{{""}};
  9. while (!q.empty()) {
  10. ++res;
  11. for (int k = q.size(); k > ; --k) {
  12. auto t = q.front(); q.pop();
  13. for (int i = ; i < t.size(); ++i) {
  14. char c = t[i];
  15. string str1 = t.substr(, i) + to_string(c == '' ? : c - '' + ) + t.substr(i + );
  16. string str2 = t.substr(, i) + to_string(c == '' ? : c - '' - ) + t.substr(i + );
  17. if (str1 == target || str2 == target) return res;
  18. if (!visited.count(str1) && !deadlock.count(str1)) q.push(str1);
  19. if (!visited.count(str2) && !deadlock.count(str2)) q.push(str2);
  20. visited.insert(str1);
  21. visited.insert(str2);
  22. }
  23. }
  24. }
  25. return -;
  26. }
  27. };

参考资料:

https://leetcode.com/problems/open-the-lock

https://leetcode.com/problems/open-the-lock/discuss/110230/BFS-solution-C++

https://leetcode.com/problems/open-the-lock/discuss/110237/Regular-java-BFS-solution-and-2-end-BFS-solution-with-improvement

LeetCode All in One 题目讲解汇总(持续更新中...)

[LeetCode] Open the Lock 开锁的更多相关文章

  1. [LeetCode] 752. Open the Lock 开锁

    You have a lock in front of you with 4 circular wheels. Each wheel has 10 slots: '0', '1', '2', '3', ...

  2. java模拟开锁

    java模拟开锁 service qq:928900200 Introduction to Computer Science II: CSCI142Fall 2014Lab #1Instructor: ...

  3. Java并发编程之Lock(同步锁、死锁)

    这篇文章是接着我上一篇文章来的. 上一篇文章 同步锁 为什么需要同步锁? 首先,我们来看看这张图. 这是一个程序,多个对象进行抢票. package MovieDemo; public class T ...

  4. [C#基础]说说lock到底锁谁?

    写在前面 最近一个月一直在弄文件传输组件,其中用到多线程的技术,但有的地方确实需要只能有一个线程来操作,如何才能保证只有一个线程呢?首先想到的就是锁的概念,最近在我们项目组中听的最多的也是锁谁,如何锁 ...

  5. Coarse-Grained lock 粗粒度锁

    用一个锁Lock一组相关的对象 有时,需要按组来修改多个对象. 这样,在需要锁住其中一个的时候,必须连带地将其他的对象都上锁. 为每一个对象都加上一个锁是很繁琐的. 粗粒度锁是覆盖多个对象的单个锁. ...

  6. 4位开锁<dfs>

    题意: 有一个四位密码的锁,每一位是1~9的密码,1跟9相连.并且相邻的连个密码位可以交换.每改变一位耗时1s,给出锁的当前状态和密码,求最少解锁时间. 思路: 用bfs枚举出所有相邻交换的情况,并记 ...

  7. Java 线程锁机制 -Synchronized Lock 互斥锁 读写锁

    (1)synchronized 是互斥锁: (2)ReentrantLock 顾名思义 :可重入锁 (3)ReadWriteLock :读写锁 读写锁特点: a)多个读者可以同时进行读b)写者必须互斥 ...

  8. hihocoder 1075 : 开锁魔法III

    描述 一日,崔克茜来到小马镇表演魔法. 其中有一个节目是开锁咒:舞台上有 n 个盒子,每个盒子中有一把钥匙,对于每个盒子而言有且仅有一把钥匙能打开它.初始时,崔克茜将会随机地选择 k 个盒子用魔法将它 ...

  9. JUC--Callable 以及Lock同步锁

    /** * 一.创建执行线程的方式三:实现Callable接口.相较于实现Runnable接口方式,方法可以有返回值,并且可以抛出异常 * 二.callable 需要FutureTask实现类的支持. ...

随机推荐

  1. C++顺序容器知识总结

    容器是一种容纳特定类型对象的集合.C++的容器可以分为两类:顺序容器和关联容器.顺序容器的元素排列和元素值大小无关,而是由元素添加到容器中的次序决定的.标准库定义了三种顺序容器的类型:vector.l ...

  2. Matlab绘图基础——用print函数保存图片(Print figure or save to file)

    print(figure_handle,'formats','-rnumber','filename')  %将图形保存为png格式,分辨率为number的(默认为72),最好指定的分辨率大一点,否则 ...

  3. sys用户密码丢失找回密码的步骤和命令

    假设你的sys用户密码丢失,写出找回密码的步骤和命令? 1.确认哪个数据库实例的sys用户密码丢失:(例:数据库实例为orclA) 2.进入数据库实例的目录中找到PWDorclA.ora文件:(例目录 ...

  4. 工作流Activiti5.13学习笔记(一)

    了解工作流 1.工作流(Workflow),就是“业务过程的部分或整体在计算机应用环境下的自动化”,它主要解决的是“使在多个参与者之间按照某种预定义的规则传递文档.信息或任务的过程自动进行,从而实现某 ...

  5. Bean validation

    公司测试非常严格,要求我们对每个参数的长度进行校验,提了一个参数长度校验的单,然后我们老大就把我们的代码全部打回去了.... 一个bean类中往往有超多变量,如果一个个写if else,够呛,而且圈复 ...

  6. 听翁恺老师mooc笔记(13)--类型定义和联合

    typedef 虽然我们知道使用struct这个关键字定义一个结构类型,然后可以使用该结构类型定义变量.但是每次要使用的时候都需要带着struct这个关键字,那么如何摆脱这个关键字哪?C语言提供了一个 ...

  7. C语言博客作业--一二维数组

    一.PTA实验作业 题目1(7-6) (1).本题PTA提交列表 (2)设计思路 //天数n:数组下标i:小时数h,分钟数m:对应书号的标签数组flag[1001] //总阅读时间sum初始化为0,借 ...

  8. Beta冲刺集合

    1.Day1 http://www.cnblogs.com/bugLoser/p/8075868.html 2.Day2 http://www.cnblogs.com/bugLoser/p/80758 ...

  9. Beta冲刺第一天

    一.昨天的困难 Beta阶段第一天,主要进行本阶段的计划和任务分配,主要问题是上阶段所做的测试工作较少,本阶段需要加强测试工作,并不断修复检测出来的BUG. 二.今天进度 所有成员写简单测试测试整体应 ...

  10. JAVA序列化基础知识

    1.序列化是干什么的? 简单说就是为了保存在内存中的各种对象的状态(也就是实例变量,不是方法),并且可以把保存的对象状态再读出来.虽然你可以用你自己的各种各样的方法来保 存object states, ...