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:

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

Example 2:

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

Example 3:

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

Example 4:

Input: deadends = ["0000"], target = "8888"
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()就没事,参见代码如下:

解法一:

class Solution {
public:
int openLock(vector<string>& deadends, string target) {
unordered_set<string> deadlock(deadends.begin(), deadends.end());
if (deadlock.count("")) return -;
int res = ;
unordered_set<string> visited{{""}};
queue<string> q{{""}};
while (!q.empty()) {
++res;
for (int k = q.size(); k > ; --k) {
auto t = q.front(); q.pop();
for (int i = ; i < t.size(); ++i) {
for (int j = -; j <= ; ++j) {
if (j == ) continue;
string str = t;
str[i] = ((t[i] - '') + + j) % + '';
if (str == target) return res;
if (!visited.count(str) && !deadlock.count(str)) q.push(str);
visited.insert(str);
}
}
}
}
return -;
}
};

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

解法二:

class Solution {
public:
int openLock(vector<string>& deadends, string target) {
unordered_set<string> deadlock(deadends.begin(), deadends.end());
if (deadlock.count("")) return -;
int res = ;
unordered_set<string> visited{{""}};
queue<string> q{{""}};
while (!q.empty()) {
++res;
for (int k = q.size(); k > ; --k) {
auto t = q.front(); q.pop();
for (int i = ; i < t.size(); ++i) {
char c = t[i];
string str1 = t.substr(, i) + to_string(c == '' ? : c - '' + ) + t.substr(i + );
string str2 = t.substr(, i) + to_string(c == '' ? : c - '' - ) + t.substr(i + );
if (str1 == target || str2 == target) return res;
if (!visited.count(str1) && !deadlock.count(str1)) q.push(str1);
if (!visited.count(str2) && !deadlock.count(str2)) q.push(str2);
visited.insert(str1);
visited.insert(str2);
}
}
}
return -;
}
};

参考资料:

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. 关于Unity中如何代码动态修改天空盒

    在Unity中动态修改天空盒有两种方法: 一.为每个Texture建立天空盒材质球,需要更换时直接将对应材质球作为天空盒,缺点是建立的材质球太多 private void ChangeSkybox(M ...

  2. bootstrap 模态框(modal)插件使用

    今天用户登陆时,在原网页上弹出新登陆窗口,发现使用的是modal插件,记录下该插件的使用方法,手写强化下. 首先,模态框(modal)是覆盖在父窗体上的子窗体,目的是显示来自一个单独的源的内容,可以在 ...

  3. Mybatis学习笔记一

    Mybatis介绍 MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为M ...

  4. oracle数据库修改连接数

    最近在用weblogic部署项目,同时用的是oracle数据库,然后今天遇到一个问题:多个用户连接数据库连接不成功,有时提示被锁住,经检查发现一方面weblogic控制台中数据源的连接池配置没有配置足 ...

  5. C语言——第二次作业(2)

    作业要求一 PTA作业的提交列表 作业要求二 题目1.删除字符串中数字字符(函数题) 1.设计思路 - (1)算法 第一步:调用定义的函数. 第二步:定义i=0.j=0,i为原字符数组角标,j为删除后 ...

  6. java第5章学习总结

    学号20145336 <Java程序设计>第5周学习总结 教材学习内容总结 try catch JVM会先尝试执行try区块中的内容,若发生错误且与catch后面的类型相符,则执行catc ...

  7. IQKeyboardManager使用方法

    使用方法: 将IQKeyboardManager 和 IQSegmentedNextPrevious类文件加进项目中.在AppDelegate文件中写下以下一行代码: [IQKeyBoardManag ...

  8. JFinal项目发送邮件——jfinal-mail-plugin

    JFianl框架: JFinal 是基于 Java 语言的极速 WEB + ORM 框架,其核心设计目标是开发迅速.代码量少.学习简单.功能强大.轻量级.易扩展.Restful.在拥有Java语言所有 ...

  9. Python 迭代器之列表解析

     [TOC] 尽管while和for循环能够执行大多数重复性任务, 但是由于序列的迭代需求如此常见和广泛, 以至于Python提供了额外的工具以使其更简单和高效. 迭代器在Python中是以C语言的 ...

  10. JAVA_SE基础——27.匿名对象

    黑马程序员入学blog... 匿名对象:没有引用类型变量指向的对象称作为匿名对象. 匿名对象要注意的事项:1. 我们一般不会给匿名对象赋予属性值,因为永远无法获取到.2. 两个匿名对象永远都不可能是同 ...