5314跳跃游戏IV

题目:给你一个整数数组 arr ,你一开始在数组的第一个元素处(下标为 0)。每一步,你可以从下标 i 跳到下标:
    i + 1 满足:i + 1 < arr.length
    i - 1 满足:i - 1 >= 0
    j 满足:arr[i] == arr[j] 且 i != j
请你返回到达数组最后一个元素的下标处所需的 最少操作次数 。注意:任何时候你都不能跳到数组外面。

来源:https://leetcode-cn.com/problems/jump-game-iv/

法一: 自己的超时代码    dfs

思路:利用回溯的方法,把每次要遍历的记入k中,同时记录每个分支遍历过的索引,防止重复遍历,剪枝条件是:如果某次回溯的时候,某个到达个位置的次数大于之前记录的值了,直接结束回溯。这个题无法用dp,解题的时候要快速识别出无法用dp动态规划来解决。或即使用dp也很复杂。

from typing import List
from collections import defaultdict
from functools import lru_cache
class Solution:
    def minJumps(self, arr: List[int]) -> int:
        # print('3333333333', len(arr))
        d = defaultdict(list)
        size = len(arr)
        for i,j in enumerate(arr):
            d[j] = d[j] + [i]
        # @lru_cache(None)
        memo = defaultdict(lambda : size)
        def recursion(index, cou=0, used=[-1,0]):
            # 回溯结束条件
            if index == size-1:
                # print('*' * 8, cou)
                return cou
            # 加上这个剪枝条件后,仍然超时
            # 这个条件是如果现在的cou大于之前记录的值,则直接返回,结束这次回溯
            if memo[index] < cou:
                print('-'* 30)
                return size
            # if index == 0:
            #     k = [1] + d[arr[0]][1:]
            #     # print('kkkkkkkkkkkkkkkkk')
            # else:

            # used [-1,0]的初始值中的-1是为了处理index=0时的情况
            # 这儿之所以取set是因为有重复值的情况,
            k = list(set(([index-1] if index-1 not in used else []) 
              + ([index+1] if index+1 not in used else []) 
              + list(set(d[arr[index]]) - set(used))))
            # 当k为空的时候说明,两边都走过了,也无法跳跃了,直接返回
            if k == []:
                return cou
            memo[index] = cou
            res = size
            cou += 1
            for j in k:
                # 这里是关键,每次回溯的时候,传递下一次要跳到的地方,跳过的步数,以及已经跳过的位置,
                # 如果没有这个used,程序会陷入死循环,实际上是为了防止重复遍历
                res = min(res, recursion(j, cou=cou, used=used+[j]))
            return res
        return recursion(index=0)
if __name__ == '__main__':
    duixiang = Solution()
    # a = duixiang.minJumps(arr = [100,-23,-23,404,100,23,23,23,3,404])
    a = duixiang.minJumps(arr = [68,-94,-44,-18,-1,18,-87,29,-6,-87,-27,37,-57,7,
                                 18,68,-59,29,7,53,-27,-59,18,-1,18,-18,-59,-1,-18,
                                 -84,-20,7,7,-87,-18,-84,-20,-27])
    print(a)
View Code

法二:bfs   别人的代码

思路:这个题是一个非常典型的最短路径问题,天然具有应用bfs的优势,因为在这个最短路径问题中,每走一步都是在向目标靠近,而对于每个位置而言,从出发点第一次到达该位置的步数即到达该点的最小值,所以无需再次遍历,由此便想到用bfs,将要走的位置放在队列的前面,由此便控制住了步数,最先到达末尾的即最短的步数。

from collections import defaultdict
from typing import List
class Solution:
    def minJumps(self, arr: List[int]) -> int:
        d = defaultdict(list)
        n = len(arr)
        for i in range(n):
            d[arr[i]].append(i)
        vis = [0] * n
        vis[0] = 1
        q = [(0, 0)]
        while q:
            # i是当前位置的索引,k是走过的步数,
            i, k = q.pop(0)
            # 遍历当前位置的上一个和下一个,以及可以跳跃的位置
            for j in d[arr[i]] + [i - 1, i + 1]:
                # 如果出界或者已经遍历过了,直接进行下一个循环
                if j < 0 or j >= n or vis[j]:
                    continue
                # 一旦跳到最后一个位置了,则它即为最少的步数,直接返回结果
                if j == n - 1:
                    return k + 1
                # 遍历过的标记为1
                vis[j] = 1
                # 把当前的位置放入队列,并且把步数加1
                q.append((j, k + 1))
            # 某个值的索引一旦遍历过其中一个后,就置为[],因为其余的索引已经放入队列了,无需再次遍历
            # 如果第二次在相同的值之间跳跃,则浪费了步数,肯定不是最短的
            d[arr[i]] = []
        return 0
View Code

ttt

原文地址:https://www.cnblogs.com/xxswkl/p/12288691.html