快速上手leetcode动态规划题

快速上手leetcode动态规划题

我如今是初学的状态,在此来记录个人刷题过程,便于之后复习巩固。java

我leetcode从动态规划开始刷,语言用的java。数组

一.了解动态规划

我上网查了一下动态规划,了解到动态规划是“带有备忘录的递归”,优化

而大多数用来理解动态规划的例子都是斐波那契数列,就是那个经典的递归式spa

f(i)=f(i-1)+f(i-2) ,f(1)=f(2)=1code

那么咱们就能够获得不少式子,好比求f(5):blog

f(5)=f(4)+f(3);递归

f(4)=f(3)+f(2);索引

f(3)=f(2)+f(1);leetcode

而后咱们就发现了重复的部分,在求f(5)和f(4)的时候都要求f(3),那么它们都要作一次f(3)的递归操做,来获得f(3)的值。get

我想这是很不值得的,不必一样的操做执行两遍。而且咱们知道当f(n)的n比较大时,是不少重复的部分的,这也就意味着有很大的优化空间。

所以有了所谓的“备忘录”,也就是用一个数组来记录每一个状态的结果,好比f(5)就是n为5时f(n)的状态。

这样的话,咱们就能够在求f(n)的时候,先查看一下数组中是否记录了这一个状态的值,若是有,就直接从数组中拿,若是没有,就递归计算一下,再把这个值放到数组中去。这也是所谓的“以空间换时间”的思想。

int[] dp=new int[n+1];//dp[i]表示f(i)的值

在求f(x)时:

if(dp[x]==0)//未被记录到数组

dp[x]=f(x-1)+f(x-2)

return dp[x];

同时,递归也是会花费不少时间的,咱们可否换一种方式呢?

这时候咱们发现f(n)的状态之间存在递推关系,也就是f(n)=f(n-1)+f(n-2)

那么这就对应了动态规划的第二个关键因素状态转移方程,咱们把递推关系转化成数组dp先后的关系,

好比斐波拉契数列的就是dp[i]=dp[i-1]+dp[i-2]

有了这个方程,咱们就能够循环求dp[i]的值了

dp[5]=dp[4]+dp[3],

dp[4]=dp[3]+dp[2],

dp[3]=dp[2]+dp[1];

那么在求dp[5]的时候dp[4]和dp[3]已是保存在数组了,即可以直接得到。

咱们知道递推和递归同样,须要出口,也就是递归或递推到底的标志

在这道题中出口就是dp[1]=dp[2]=1;

有了这两个值,在循环的时候咱们就能够求出全部的值了,这就是出口的意义。

感受能够类比数学里数学概括法

//须要先作个判断

if(n==1||n==2)

return 1;

dp[1]=dp[2]=1;

for(int i=3;i<n+1;i++)

{

dp[i]=dp[i-1]+dp[i-2];

}

return dp[n];

最后,我以为重要的就是把握总体的边界状况,好比这里的n==1和n==2是不用递推关系的,并且dp[1]=dp[2]=1以前须要肯定n>2才能赋值的,有的题目里还有给出参数为一个数组,这时须要考虑数组长度为0的状况等等

最后总结一下动态规划的四个要素(本身总结的):

1.定义数组

2.找出递推关系

3.找出出口

4.把握总体边界

它们在程序中的位置是4->1->3->2

最后返回值

2、刷题练习

70. 爬楼梯

假设你正在爬楼梯。须要 n 阶你才能到达楼顶。

每次你能够爬 1 或 2 个台阶。你有多少种不一样的方法能够爬到楼顶呢?

注意:给定 n 是一个正整数。

示例 1:

输入: 2
输出: 2
解释: 有两种方法能够爬到楼顶。

  1. 1 阶 + 1 阶

  2. 2 阶

示例 2:

输入: 3
输出: 3
解释: 有三种方法能够爬到楼顶。

  1. 1 阶 + 1 阶 + 1 阶

  2. 1 阶 + 2 阶

  3. 2 阶 + 1 阶

解答:

思路

1.设置数组

经过题目,咱们知道咱们最终要求的是到达n阶有多少种方法,那不妨就设这个为dp[n]

那咱们要求的数组dp[i]表示的就是到达i阶时有dp[i]种方法

2.找出递推关系:

那咱们就要了解dp[i]是怎么来的了?

根据题目,咱们知道,每次是能够跨一阶或者两阶的,那么dp[i]就只有两种方法获得

一种是由dp[i-2]跨两阶来的,还有一种是dp[i-1]跨一阶来的。

那么dp[i]的方法数应该等于dp[i-1]的加上dp[i-2]了。

所以,咱们找出了递推关系dp[i]=dp[i-1]+dp[i-2]

3.找出出口

根据递推式咱们知道i>=2才能使用递推获得,否则下标就要<0了

那咱们求一下出口dp[0]=0,0阶的时候确定只有0种方法

dp[1]=1;1阶的时候只有跨1阶这一种方法。

可是这里还有一个dp[2]也是出口,可能被忽略掉,由于按照递推式dp[2]=dp[1]+dp[0]=1,而实际上dp[2]=2

4.把握总体边界:

n<=0,n==1,和n==2能够提早算出

代码

class Solution {

  public int climbStairs(int n) {

    //1.考虑总体边界

    if(n<=0)

    return 0;

    if(n==1||n==2)

    return n;

 

    //2.设置数组

    int []dp=new int[n+1];//dp[i]表示到达i阶,有dp[i]种方法

 

    //3.考虑数组边界值

    dp[0]=0;

    dp[1]=1;

    dp[2]=2;//注意2也是边界

 

    //4.找出dp[i]与dp[i-1]的关系,循环获取所要得到的项dp[n];

    //dp[i]=dp[i-1]+dp[i-2]

    //要到达n阶能够有两种方法:一种是从i-1爬1阶来的,还有一种是i-2爬2阶来的

    //所以须要求这两种方法之和

    for(int i=3;i<=n;i++)

    {

      dp[i]=dp[i-1]+dp[i-2];

    }

    return dp[n];

  }

}

 

746. 使用最小花费爬楼梯

数组的每一个索引作为一个阶梯,第 i个阶梯对应着一个非负数的体力花费值 costi

每当你爬上一个阶梯你都要花费对应的体力花费值,而后你能够选择继续爬一个阶梯或者爬两个阶梯。

您须要找到达到楼层顶部的最低花费。在开始时,你能够选择从索引为 0 或 1 的元素做为初始阶梯。

示例 1:

输入: cost = [10, 15, 20]
输出: 15
解释: 最低花费是从cost[1]开始,而后走两步便可到阶梯顶,一共花费15。

示例 2:

输入: cost = [1, 100, 1, 1, 1, 100, 1, 1, 100, 1]
输出: 6
解释: 最低花费方式是从cost[0]开始,逐个通过那些1,跳过cost[3],一共花费6。

注意:

cost 的长度将会在 [2, 1000]。
每个 cost[i] 将会是一个Integer类型,范围为 [0, 999]

解答:

思路

1.设置数组

经过题目,咱们知道咱们最终要求的是到达n阶的最低花费,那不妨就设这个为dp[n]

那咱们要求的数组dp[i]表示的就是到达i阶时的最低花费,根据示例,咱们可知最后要返回的结果应该是dp[len]

2.找出递推关系:

首先咱们分析题目可知,消耗的体力值应该等于原来的加上到达的那一阶的体力值,所以若是跨两阶的话,应该是直接加上两阶中的后一阶的体力值的。

由于一次只能跨一阶或者两阶,所以dp[i]应该是dp[i-2]和dp[i-1]中比较小的那个加上i对应的体力值,即cost[i];

所以dp[i]=Math.min(dp[i-2],dp[i-1])+cost[i]

3.找出出口

根据递推式下标咱们知道i>1才能使用递推式

那么就须要求出dp[0]和dp[1]

dp[0]=cost[0];//到达0阶时的花费,只有一个

dp[1]=cost[1];//到达1阶一种是一阶一阶上,即cost[0]+cost[1],还有一种是直接上两阶cost[1],cost[1]更小

4.把握总体边界:

参数是一个cost数组,咱们须要考虑数组长度为0的状况,递推式不覆盖下标为0和1的,所以也应该拿出来做为出口

    if(len==0)

    return 0;

    if(len==1)

    return cost[0];

    if(len==2)

    return cost[1];

代码

class Solution {

  public int minCostClimbingStairs(int[] cost) {

    //设置出口

    int len=cost.length;

    if(len==0)

    return 0;

    if(len==1)

    return cost[0];

    if(len==2)

    return cost[1];

 

    //设置数组

    int []dp=new int[len];//dp[i]表示到达第i阶时所花费的最小体力值

``   

    //设置数组边界

    dp[0]=cost[0];

    dp[1]=cost[1];

 

    //找出数组的递推关系

    int i;

    for(i=2;i<len;i++)

    {

      dp[i]=Math.min(dp[i-2],dp[i-1])+cost[i];

    }

 

    //返回值

    return Math.min(dp[i-2],dp[i-1]);

  }

}

相关文章
相关标签/搜索