2021.05.05青蛙过河

news/2024/11/22 22:43:54/

2021.05.05青蛙过河

(题目来源:https://leetcode-cn.com/problems/frog-jump/)

题目描述

一只青蛙想要过河。 假定河流被等分为若干个单元格,并且在每一个单元格内都有可能放有一块石子(也有可能没有)。 青蛙可以跳上石子,但是不可以跳入水中。

给你石子的位置列表 stones(用单元格序号 升序 表示), 请判定青蛙能否成功过河(即能否在最后一步跳至最后一块石子上)。

开始时, 青蛙默认已站在第一块石子上,并可以假定它第一步只能跳跃一个单位(即只能从单元格 1 跳至单元格 2 )。

如果青蛙上一步跳跃了 k 个单位,那么它接下来的跳跃距离只能选择为 k - 1、k 或 k + 1 个单位。 另请注意,青蛙只能向前方(终点的方向)跳跃。

思路

【TLE】思路1:dfs

  1. 通过遍历所有跳石头的情况,直到跳到最后一块石头,返回true。
  2. 由于是盲目地跳石头,会出现多次通过k步跳到第i块石头的状态

【TLE】思路2:动态规划

  1. 为了防止盲目,记下之前的一些子状态。
  2. 定义状态:dp[i][j] :能通过第i块石头跳到第j块石头。跳的步数为arr[j]-arr[i]。
  3. 状态转移方程:
    若满足:
    k = = ( l a s J u m p ) = = > i = = ( c u r J u m p ) = = > j k ==(lasJump)==> i ==(curJump)==> j k==(lasJump)==>i==(curJump)==>j
    且:
    l a s = = c u r ∣ ∣ l a s = = c u r − 1 ∣ ∣ l a s = = c u r + 1 las == cur || las == cur-1 || las == cur+1 las==curlas==cur1las==cur+1
    则:
    d p [ i ] [ j ] = d p [ i ] [ j ] ∣ ∣ d p [ k ] [ i ] ; dp[i][j] = dp[i][j] || dp[k][i]; dp[i][j]=dp[i][j]dp[k][i];

但是,由于找到前一块石头需要遍历,所以导致TML。

【优化】思路2:动态规划+二分查找

查找前前个石头时,采用二分查找
优化后代码如下:

	public boolean canCross(int[] arr) {int n = arr.length;if(n == 2) return arr[1]-arr[0] == 1;//定义状态://dp[i][j] == true:第i到第j个是可以到达的,可以计算步数k = arr[j]-arr[i]  (i < j)//k ==(arr[i]-arr[k])==>  i  ==(arr[j]-arr[i])==>  j  boolean[][] dp = new boolean[n][n]; dp[0][0] = true; dp[0][1] = (arr[1]-arr[0] == 1);for(int j = 1; j < n; j++) {for(int i = j-1; i >= 1; i--) {int cur = arr[j]-arr[i];//上一步是 cur / cur-1 / cur+1if(cur-1>i) break; //优化,提前跳出该情况//上一步的所有可能步长for(int las = cur-1; las <= cur+1; las++) {int ind = Arrays.binarySearch(arr, 0, i, arr[i]-las);if(ind >= 0) dp[i][j] = dp[i][j] || dp[ind][i];}if(j == n-1 && dp[i][j]) return true; }}return false;}

思路3:动态规划

  1. 为了找到更好的状态转移方程以防止需要循环查找前前个石头,需要改变状态定义。
  2. 定义状态 dp[i][k] : 能否通过跳k步跳到第i块石头。(由于步长每次最多递增1步的限制,所以状态矩阵的第二维也不会超过n。
  3. 状态转移方程:
    (1)状态转移描述
    假设从第j块石头跳到第i块石头跳了k步,但是k必须满足“跳到第j块石头是通过跳了k或k-1或k+1步”
    (2)将描述通过方程表达

    dp[i][k] = dp[j][k] || dp[j][k-1] || dp[j][k+1]

代码如下:

	public boolean canCross(int[] stones) {int n = stones.length;//定义状态://dp[i][j] == true:到达第i个单元,跳了k步,boolean[][] dp = new boolean[n][n]; dp[0][0] = true;for(int i = 1; i < n; i++) {for(int j = i-1; j >= 0; j--) {//跳的步数int k = stones[i] - stones[j]; //上一次跳的最少步数为k-1,如果最少步数都超过了j,则说明不符合if(k-1 > j) break; dp[i][k] = dp[j][k-1] || dp[j][k] || dp[j][k+1];//可以在循环中判断,也可以在dp矩阵初始化完成后再判断。if(i == n-1 && dp[i][k] == true) return true;}}return false;}

思路4:记忆化搜索+Map

  1. 由于Map查找效率高,可以完成以下操作:
    (1)存储历史状态,查找当前处理的情况之前有没有已经处理过。
    (2)查找一个位置是否有石头,即一个数x是否在stones[]数组中。
  2. 进行dfs搜索。

代码:

	//<石头的位置, 数组中下标>Map<Integer, Integer> map ; //用于存放石头的位置,方便之后查找Map<Integer, Boolean> cache; //存储的是之前遍历的结果public boolean canCross(int[] stones) {map = new HashMap<>();cache =new HashMap<>();for (int i = 0; i < stones.length; i++) {map.put(stones[i],i);}//默认 第一位置只能跳1个单位return dfs(stones,0,1);}private boolean dfs(int[] stones,int start,int k){// key的映射函数Integer key = stones[start] * 2000 * 2000 + k;//如果之前已经处理过该状态if(null != cache.get(key)) return cache.get(key);//查找下一个位置是否存在int index = map.getOrDefault(stones[start] + k,-1);//跳不到 || 回头跳 || 原地跳if(index == -1 || index <= start) {cache.put(key, false);return false;}// 跳到最后if(index == stones.length-1) {cache.put(key, true);return true;}// 跳k k-1 k+1 只要任意一个跳到就可以boolean b = dfs(stones, index, k + 1) || dfs(stones, index, k) || dfs(stones, index, k - 1);cache.put(key,b);return b;}

2021.5.19题外话:

再看此种解法,产生了以下问题:

  1. 为何需要记录一个key(start, step)的映射?
    答:在定义状态时,我们定义决定一个状态共有两个参数,即当前的位置i和跳到i块石头的步数step,所以为了避免重复遍历状态,我们需要建立key(start, step)的映射函数。
    即对于状态(i , k),如果其不能遍历到终点,我们回溯时返回false,其他分支遍历过程中再次遇到此状态,可以直接返回false。
  2. 会不会根本不会出现重复的“从j跳k步到i”的状态呢,只会出现“重复遍历到位置i”。
    答:错。
    若按照上述依据写代码:

在这里插入图片描述

仍报超时错误。故再思考。
参考别人的图解,以[1,2,3,4,5,999]为例,见图:

在这里插入图片描述](https://img-blog.csdnimg.cn/d999be69fa884b94bc42275fbd22dd4b.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAWmFja196Y196Yw==,size_17,color_FFFFFF,t_70,g_se,x_16)

在这里插入图片描述


http://www.ppmy.cn/news/917869.html

相关文章

小学计算机听课总评,小学听课评课评语大全

小学听课评课评语大全 听课评课&#xff0c;属于一种对课堂进行仔细观察的活动。能提高教师素质。以下是小编整理的小学听课评课评语大全&#xff0c;欢迎阅读&#xff01; 能抓住课本重难点&#xff0c;对生态系统的组成和营养结构作详细讲解&#xff0c;课堂气氛活跃&#xf…

垂钓湾

昨天甜甜刚背了一首 江雪,今天又背了类似垂钓的一首 垂钓湾 垂钓绿湾春 春深杏花乱 潭清疑水浅 荷动知鱼散 日暮待情人 维舟绿杨岸 网上随意搜了一下,有个书写垂钓的文章: http://fengguang.blog.sohu.com/44579056.html 好多垂钓的名诗名句..

青蛙跳荷叶

青蛙跳荷叶 题目大意&#xff1a; 有n个点&#xff0c;从1开始到跳完这些点&#xff0c;且每次的距离不能相等&#xff0c;一个点不能到多次 原题&#xff1a; 题目描述 从前&#xff0c;有一个小青蛙决定去荷叶上练习跳跃.现在有n个荷叶排成一排&#xff0c;小青蛙一开始…

基于Spring Boot垂钓服务系统的设计与实现毕业设计源码071739

目 录 摘要 1 绪论 1.1 研究背景 1.2研究意义 1.3相关技术介绍 1.4论文结构与章节安排 2 垂钓服务系统需求分析 2.1 可行性分析 2.1.1 技术可行性分析 2.1.2 经济可行性分析 2.1.3 操作可行性分析 2.1.4 法律可行性分析 2.2 系统流程分析 2.2.1 数据流程 3.3.2 …

集体备课模板_小学语文集体备课教案模板(共5篇)

第1篇&#xff1a;小学集体备课教案 小学集体备课教案 课题 长方体和正方体的认识 主备人 王世平 教学 目标 知识目标 让学生了解体积的概念和体积单位&#xff0c;感知长方体和正方体体积单位的大小。 能力目标 动手操作&#xff0c;正确推导出长方体和正方体的体积公式&#…

两小儿辩“日”

昨天&#xff0c;野猪给我看了gameres上一个人气帖&#xff0c;标题叫作《有没有关于d3d底层的资料》 有人在帖子里讨论d3d内部工作流程&#xff0c;结果2个强人在帖子中旁若无人&#xff0c;大顶日文&#xff0c; 被人说后&#xff0c;理直气壮&#xff0c;决定并没有影响别…

快乐垂钓记

我、文洁长和周团书是同班同学&#xff0c;也是好朋友。我担任我们班的学习委员&#xff0c;文洁长是团支部书记&#xff0c;周团书是班长。中考前一天下午&#xff0c;我就对我的好友文洁长说&#xff1a;“你是垂钓高手&#xff0c;中考结束的第二天下午你带我和周团书一同前…

管理寓言之六:垂钓者的启示

垂钓者的启示&#xff08;输得有道理&#xff09; 经常&#xff0c;有人觉得自己的条件与竞争对手相同&#xff0c;甚至乎&#xff0c;还比对方优胜&#xff0c;何解&#xff0c;成绩总是不及他的呢&#xff1f; 这儿有个心灵故事&#xff0c;希望大家读后&#xff0c;能启发…