你前面有一把锁,有四个轮子。每个轮子有10个槽:“0”、“1”、“2”、“3”、“4”、“5”、“6”、“7”、“8”、“9”。轮子可以自由旋转和环绕:例如,我们可以把’9’变成’0’,或’0’变成’9’。每一步包括转动一个轮子和一个插槽。
锁最初从“0000”开始,这是一个表示四个轮子状态的字符串。
你会得到一个死锁的列表,这意味着如果锁显示了这些代码中的任何一个,锁的轮子将停止转动,你将无法打开它。
给定一个表示将解锁的轮子的值,返回打开锁所需的最小总匝数,如果不可能,返回-1。
代码
解题思路:锁初始化是0000,每次能转动一次,所以锁的状态每次都会变化一次,4个值中的某个值加1或者是减1。所以,每个数值,都可以产生出8(4*2)种不同的值出来,这8个值又可以产生出64(8*8)种不同的值出来,这样一直下去,直到找到所需要的值或者到遍历完所有可能的值为止。在遍历查找的过程当中,我们要规避会造成死锁的某些值,并要找到循环退出的条件。
class Lock
{
public:
int openLock(vector<string>& deadends, string target) {
int result = -1;
string init("0000");
queue<string> q;
unordered_set<string> deadendset(deadends.begin(), deadends.end());
unordered_set<string> s;
q.push(init);
s.insert(init);
while (!q.empty()) {
++ result;
int size = q.size();
for (int i = 0; i < size; ++i) {
string str = q.front();
q.pop();
if (target == str) {
return result;
}
if (deadendset.end() == deadendset.find(str)) {
for (unsigned j = 0; j < str.size(); ++j) {
string case1(str);
case1[j] = (case1[j] + 1 - 48) % 10 + 48;
if (s.end() == s.find(case1)) {
q.push(case1);
s.insert(case1);
}
string case2(str);
case2[j] = (case2[j] + 9 - 48) % 10 + 48;
if (s.end() == s.find(case2)) {
q.push(case2);
s.insert(case2);
}
}
}
}
}
return -1;
}
};
在上述代码中,我们每次遍历队列当中的数值,根据这些数据来判断是否是需要的目标值,或者是死锁的值。如果是需要的值则直接返回我们遍历的次数,否则,继续去除队列当中会造成死锁的值,把剩下的值放入到队列当中去,留着下次一起进行上述的循环判断。在这里使用了一个集体来保存已经校验过的值,免得循环一直进行( 没有找到目标值则不会退出 ),这样可以规避掉陷入死循环。
测试
example 1:
deadends = ["0201","0101","0102","1212","2002"], target = "0202"
example 2:
deadends = ["8888"], target = "0009"
根据以上的测试用例来编写测试代码:
int main()
{
vector<string> deadends = {"0201", "0101", "0102", "1212", "2002"};
string target("0202");
//vector<string> deadends = { "8888"};
//string target("0009");
Lock lock;
int times = lock.openLock(deadends, target);
cout << times << endl;
return 0;
}