Leetcode练习:打开转盘锁,python实现

题目描述:

你有一个带有四个圆形拨轮的转盘锁。每个拨轮都有10个数字: '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' 。每个拨轮可以自由旋转:例如把 '9' 变为  '0','0' 变为 '9' 。每次旋转都只能旋转一个拨轮的一位数字。

锁的初始数字为 '0000' ,一个代表四个拨轮的数字的字符串。

列表 deadends 包含了一组死亡数字,一旦拨轮的数字和列表里的任何一个元素相同,这个锁将会被永久锁定,无法再被旋转。

字符串 target 代表可以解锁的数字,你需要给出最小的旋转次数,如果无论如何不能解锁,返回 -1。

 

示例 1:

输入:deadends = ["0201","0101","0102","1212","2002"], target = "0202"
输出:6
解释:
可能的移动序列为 "0000" -> "1000" -> "1100" -> "1200" -> "1201" -> "1202" -> "0202"。
注意 "0000" -> "0001" -> "0002" -> "0102" -> "0202" 这样的序列是不能解锁的,
因为当拨动到 "0102" 时这个锁就会被锁定。
示例 2:

输入: deadends = ["8888"], target = "0009"
输出:1
解释:
把最后一位反向旋转一次即可 "0000" -> "0009"。
示例 3:

输入: deadends = ["8887","8889","8878","8898","8788","8988","7888","9888"], target = "8888"
输出:-1
解释:
无法旋转到目标数字且不被锁定。
示例 4:

输入: deadends = ["0000"], target = "8888"
输出:-1
 

提示:

死亡列表 deadends 的长度范围为 [1, 500]。
目标数字 target 不会在 deadends 之中。
每个 deadends 和 target 中的字符串的数字会在 10,000 个可能的情况 '0000' 到 '9999' 中产生。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/open-the-lock
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

 

我的想法:可以当做是四维的“墙与门”问题。和二维情况不同,这里每个元素有八个邻居。起点固定,从"0000"开始,需要注意“0”往下走是“9”,也就是说不存在边界。

可以使用广度优先遍历方法,从起始点开始,每一层将该层节点的符合条件的邻居放入下一层,然后按队列顺序再访问下一层,直到找到目标节点或者层为空为止。最后返回层数,即为最小步长。

实现后发现超时。思考了一下,由于矩阵是四维的,因此每一层的结点数增长的会非常快。结合人的直观感受,不应该无差别的将邻居节点放入队列,而应该将距离目标节点较近的节点放入,如果较近的节点都不符合条件,才将所有邻居节点放入队列。

实现后发现还是超时。思考了一下,只有目标结点被deadends完全围住的情况下,才会返回-1。按照我们的方法,必须把整个四维数组遍历完发现找不到target才返回-1,而其实直接判断一下target的八个邻居是不是全部为deadends就可以了。因此在开头加上这个判断。

成绩:

 

执行用时 :2092 ms, 在所有 Python 提交中击败了5.34%的用户

内存消耗 :11.8 MB, 在所有 Python 提交中击败了92.68%的用户

 

代码:

# for four dimension array, each item has 2*4 neighbors. It's just a 4 dimensional island question
class Solution(object):
    def isBlock(self, deadends, target):
        next_diction = {"0": "1", "1": "2", "2": "3", "3": "4", "4": "5", "5": "6", "6": "7", "7": "8", "8": "9", "9": "0"}
        last_diction = {"0": "9", "1": "0", "2": "1", "3": "2", "4": "3", "5": "4", "6": "5", "7": "6", "8": "7", "9": "8"}
        neighbor1 = next_diction[target[0]]+ target[1]+ target[2]+ target[3] 
        neighbor2 = last_diction[target[0]]+ target[1]+ target[2]+ target[3]
        neighbor3 = target[0]+ next_diction[target[1]]+ target[2]+ target[3]
        neighbor4 = target[0]+ last_diction[target[1]]+ target[2]+ target[3]
        neighbor5 = target[0]+ target[1]+ next_diction[target[2]]+ target[3]
        neighbor6 = target[0]+ target[1]+ last_diction[target[2]]+ target[3]
        neighbor7 = target[0]+ target[1]+ target[2]+ next_diction[target[3]]
        neighbor8 = target[0]+ target[1]+ target[2]+ last_diction[target[3]]
        
        if neighbor1 in deadends and neighbor2 in deadends and neighbor3 in deadends and neighbor4 in deadends and neighbor5 in deadends and neighbor6 in deadends and neighbor7 in deadends and neighbor8 in deadends:
            return -1
        else:
            return 1
        
    def pathLoss(self, current_node, target):
        distance1 = min(abs(int(current_node[0]) - int(target[0])), 10 - abs(int(current_node[0]) - int(target[0])))
        distance2 = min(abs(int(current_node[1]) - int(target[1])), 10 - abs(int(current_node[1]) - int(target[1])))
        distance3 = min(abs(int(current_node[2]) - int(target[2])), 10 - abs(int(current_node[2]) - int(target[2])))
        distance4 = min(abs(int(current_node[3]) - int(target[3])), 10 - abs(int(current_node[3]) - int(target[3])))
        return distance1 + distance2 + distance3 + distance4
    
    def openLock(self, deadends, target):
        """
        :type deadends: List[str]
        :type target: str
        :rtype: int
        """
        if "0000" in deadends or self.isBlock(deadends, target) == -1:
            return -1
        next_diction = {"0": "1", "1": "2", "2": "3", "3": "4", "4": "5", "5": "6", "6": "7", "7": "8", "8": "9", "9": "0"}
        last_diction = {"0": "9", "1": "0", "2": "1", "3": "2", "4": "3", "5": "4", "6": "5", "7": "6", "8": "7", "9": "8"}
        
        visited_list = []
        current_level = ["0000"]
        level_count = 0
        while(len(current_level)):
            new_level = []
            for current_node in current_level:
                if current_node in visited_list:
                    continue
                visited_list.append(current_node)
                current_distance = self.pathLoss(current_node, target)
                if current_node == target:
                    return level_count
                neighbor1 = next_diction[current_node[0]]+ current_node[1]+ current_node[2]+ current_node[3] 
                neighbor2 = last_diction[current_node[0]]+ current_node[1]+ current_node[2]+ current_node[3]
                neighbor3 = current_node[0]+ next_diction[current_node[1]]+ current_node[2]+ current_node[3]
                neighbor4 = current_node[0]+ last_diction[current_node[1]]+ current_node[2]+ current_node[3]
                neighbor5 = current_node[0]+ current_node[1]+ next_diction[current_node[2]]+ current_node[3]
                neighbor6 = current_node[0]+ current_node[1]+ last_diction[current_node[2]]+ current_node[3]
                neighbor7 = current_node[0]+ current_node[1]+ current_node[2]+ next_diction[current_node[3]]
                neighbor8 = current_node[0]+ current_node[1]+ current_node[2]+ last_diction[current_node[3]]
                
                short_path = False
                
                if neighbor1 not in deadends and neighbor1 not in visited_list and self.pathLoss(neighbor1, target) < current_distance:
                    short_path = True
                    new_level.append(neighbor1)
                if neighbor2 not in deadends and neighbor2 not in visited_list and self.pathLoss(neighbor2, target) < current_distance:
                    short_path = True
                    new_level.append(neighbor2)
                if neighbor3 not in deadends and neighbor3 not in visited_list and self.pathLoss(neighbor3, target) < current_distance:
                    short_path = True
                    new_level.append(neighbor3)
                if neighbor4 not in deadends and neighbor4 not in visited_list and self.pathLoss(neighbor4, target) < current_distance:
                    short_path = True
                    new_level.append(neighbor4)
                if neighbor5 not in deadends and neighbor5 not in visited_list and self.pathLoss(neighbor5, target) < current_distance:
                    short_path = True
                    new_level.append(neighbor5)
                if neighbor6 not in deadends and neighbor6 not in visited_list and self.pathLoss(neighbor6, target) < current_distance:
                    short_path = True
                    new_level.append(neighbor6)
                if neighbor7 not in deadends and neighbor7 not in visited_list and self.pathLoss(neighbor7, target) < current_distance:
                    short_path = True
                    new_level.append(neighbor7)
                if neighbor8 not in deadends and neighbor8 not in visited_list and self.pathLoss(neighbor8, target) < current_distance:
                    short_path = True
                    new_level.append(neighbor8)
                
                if short_path == False:
                    if neighbor1 not in deadends and neighbor1 not in visited_list:
                        new_level.append(neighbor1)
                    if neighbor2 not in deadends and neighbor2 not in visited_list:
                        new_level.append(neighbor2)
                    if neighbor3 not in deadends and neighbor3 not in visited_list:
                        new_level.append(neighbor3)
                    if neighbor4 not in deadends and neighbor4 not in visited_list:
                        new_level.append(neighbor4)
                    if neighbor5 not in deadends and neighbor5 not in visited_list:
                        new_level.append(neighbor5)
                    if neighbor6 not in deadends and neighbor6 not in visited_list:
                        new_level.append(neighbor6)
                    if neighbor7 not in deadends and neighbor7 not in visited_list:
                        new_level.append(neighbor7)
                    if neighbor8 not in deadends and neighbor8 not in visited_list:
                        new_level.append(neighbor8)
                    
                    
            level_count+=1
            current_level = new_level
        return -1
                    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值