80跳跃游戏 IV(1345)

作者: Turbo时间限制: 1S章节: 宽度优先搜索

晚于: 2020-08-26 12:00:00后提交分数乘系数50%

截止日期: 2020-09-02 12:00:00

问题描述 :

给你一个整数数组 arr ,你一开始在数组的第一个元素处(下标为 0)。

每一步,你可以从下标 i 跳到下标:

i + 1 满足:i + 1 < arr.length

i - 1 满足:i - 1 >= 0

j 满足:arr[i] == arr[j] 且 i != j

请你返回到达数组最后一个元素的下标处所需的最少操作次数 。

注意:任何时候你都不能跳到数组外面。

 

示例 1:

输入:arr = [100,-23,-23,404,100,23,23,23,3,404]

输出:3

解释:那你需要跳跃 3 次,下标依次为 0 --> 4 --> 3 --> 9 。下标 9 为数组的最后一个元素的下标。

 

示例 2:

输入:arr = [7]

输出:0

解释:一开始就在最后一个元素处,所以你不需要跳跃。

 

示例 3:

输入:arr = [7,6,9,6,9,6,9,7]

输出:1

解释:你可以直接从下标 0 处跳到下标 7 处,也就是数组的最后一个元素处。

 

示例 4:

输入:arr = [6,1,9]

输出:2

 

示例 5:

输入:arr = [11,22,7,7,7,7,7,7,7,22,13]

输出:3

 

输入说明 :

首先输入数组arr的长度n,

然后输入n个整数,以空格分隔。

1 <= n <= 5 * 10^4

-10^8 <= arr[i] <= 10^8

 

输出说明 :

输出一个整数,表示到达数组最后一个元素的下标处所需的最少操作次数

输入范例 :

输出范例 :

#include <iostream>
#include <vector>
#include <queue>
#include <unordered_map>
using namespace std;

class Solution 
{
public:
    int minJumps(vector<int>& arr) 
    {
        int len=arr.size();
        vector<int> dis(len,INT_MAX);
        vector<int> visited(len,0) ;//标记是否被访问 
        queue<int> q;
        unordered_map<int,vector<int>> m;
        for(int i=0;i<len-1;i++)
            m[arr[i]].push_back(i);
        
        q.push(len-1);
        dis[len-1]=0;
        
        while(!q.empty())
        {
            int front =q.front();
            q.pop();
            if(front-1>=0&&!visited[front-1]&&m.find(arr[front-1])!=m.end())
            {
                dis[front-1]=min(dis[front-1],dis[front]+1);
                visited[front-1]=1;
                q.push(front-1);
            }
            if(front+1<len&&!visited[front+1]&&m.find(arr[front+1])!=m.end())
            {
                dis[front+1]=min(dis[front+1],dis[front]+1);
                visited[front+1]=1;
                q.push(front+1);
            }
            if(m.find(arr[front])!=m.end())
            {
                for(int it:m[arr[front]])
                {
                    if(!visited[it])
                    {
                        visited[it]=1;
                        dis[it]=min(dis[front]+1,dis[it]);
                        q.push(it);
                    }
                }
                m.erase(arr[front]);//不加就会超时, 去掉标记过的,访问过的值直接清理掉 
            }
        }
        return dis[0];
    }
};


int main()
{
    vector<int> arr;
    int n,data;
    cin>>n;
    for(int i=0; i<n; i++)
    {
        cin>>data;
        arr.push_back(data);
    }
    int result=Solution().minJumps(arr);
    cout<<result;
    return 0;
}


/*
https://leetcode-cn.com/problems/jump-game-iv/solution/wei-shi-yao-bfsyi-ji-zen-yang-on-by-tian-ye/

题目要求计算从第一个元素跳到最后一个元素最少的操作次数。首先容易想到的是,坐标位于最后一个元素时,结果应为0,因为不用任何操作;对于这个元素紧挨着的左右元素(此时只存在左边的元素),它的操作数是1,因为只需要跳一次就跳到了最后;同理,对于数组里值和最后一个元素相等的那些数,他们的操作数都是1。
然后,对于倒数第二个元素,它的操作数前一步已经求得为1,它右边数的结果也已经知道了,是0;它左边的数如果不等于最后一个数,那么它跟最后一个数的距离应为2。这里距离的意思是指操作次数,即跳两次才能到达最后一个数;同样,和倒数第二个数相等的那些元素最小的操作数也都是1+1=2.
这样,可以理解为最后一个数所在层数为0,第1层就是挨着它的元素以及值和它相等的那些数,第2层就是第一层挨着的元素以及值和第一层元素相等的那些数...按层次依次往上加。因为题目要求最少操作数,对于“挨着它的元素”如果前面已经求出了距离(层数),这时就不用再去算了,因为+1之后结果只会更大。例如,用一个数组dis[]表示每一个元素对应的最小操作数,初始化一个大数,如果对于元素i它左边有dis[i-1]等于这个大数,就表明i-1位置还没有求,于是dis[i-1]=dis[i]+1;如果dis[i-1]不等于这个大数就说明左边的元素前面已经算过了,就不要再去给它+1。
为了去找数组中和某一个数相等的所有元素位置,使用HashMap来存下所有值和相应位置(列表)的映射,可以避免一遍遍遍历这个数组去寻找。
(这时候你就可以自己动手去写了,小总结:一个数组存结果,一个Map映射和一个队列来广度优先搜索)
现在写出的代码运行那几个试例是没问题的,不过提交的时候会遇到一个满页是7,最后一个数是13的测例,可能会超时。这里还可以继续优化:对于那堆7,从倒数第二个元素开始其实就已经算出了他们的距离是[2,2,...,2,1,0],每次他都会把新出现的7放入队列,然后又遍历一遍7所在的所有位置。于是可以用一个数组visited[]来记录这个数是否被访问(计算)过,如果已经访问过了就没必要再去map里遍历它对应的所有位置了。

作者:tian-ye
链接:https://leetcode-cn.com/problems/jump-game-iv/solution/wei-shi-yao-bfsyi-ji-zen-yang-on-by-tian-ye/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。*/

 

posted on 2020-09-11 22:09  Hi!Superman  阅读(273)  评论(0编辑  收藏  举报

导航