快速上手leetcode动态规划题
快速上手leetcode动态规划题
我现在是初学的状态,在此来记录我的刷题过程,便于以后复习巩固。
我leetcode从动态规划开始刷,语言用的java。
一.了解动态规划
我上网查了一下动态规划,了解到动态规划是“带有备忘录的递归”,
而大多数用来理解动态规划的例子都是斐波那契数列,就是那个经典的递归式
f(i)=f(i-1)+f(i-2) ,f(1)=f(2)=1
那么我们就可以得到很多式子,比如求f(5):
f(5)=f(4)+f(3);
f(4)=f(3)+f(2);
f(3)=f(2)+f(1);
然后我们就发现了重复的部分,在求f(5)和f(4)的时候都要求f(3),那么它们都要做一次f(3)的递归操作,来得到f(3)的值。
我想这是很不值得的,没必要同样的操作执行两遍。并且我们知道当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
最后返回值
二、刷题练习
70. 爬楼梯
假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
注意:给定 n 是一个正整数。
示例 1:
输入: 2
输出: 2
解释: 有两种方法可以爬到楼顶。
1 阶 + 1 阶
2 阶
示例 2:
输入: 3
输出: 3
解释: 有三种方法可以爬到楼顶。
1 阶 + 1 阶 + 1 阶
1 阶 + 2 阶
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]);
}
}
快速上手leetcode动态规划题的更多相关文章
- LeetCode动态规划题总结【持续更新】
以下题号均为LeetCode题号,便于查看原题. 10. Regular Expression Matching 题意:实现字符串的正则匹配,包含'.' 和 '*'.'.' 匹配任意一个字符,&quo ...
- C#LeetCode刷题-动态规划
动态规划篇 # 题名 刷题 通过率 难度 5 最长回文子串 22.4% 中等 10 正则表达式匹配 18.8% 困难 32 最长有效括号 23.3% 困难 44 通配符匹配 17.7% ...
- LeetCode刷题总结-动态规划篇
本文总结LeetCode上有动态规划的算法题,推荐刷题总数为54道.具体考点分析如下图: 1.中心扩展法 题号:132. 分割回文串 II,难度困难 2.背包问题 题号:140. 单词拆分 II,难度 ...
- LeetCode刷题专栏第一篇--思维导图&时间安排
昨天是元宵节,过完元宵节相当于这个年正式过完了.不知道大家有没有投入继续投入紧张的学习工作中.年前我想开一个Leetcode刷题专栏,于是发了一个投票想了解大家的需求征集意见.投票于2019年2月1日 ...
- LeetCode刷题总结-数组篇(上)
数组是算法中最常用的一种数据结构,也是面试中最常考的考点.在LeetCode题库中,标记为数组类型的习题到目前为止,已累计到了202题.然而,这202道习题并不是每道题只标记为数组一个考点,大部分习题 ...
- LeetCode刷题总结-数组篇(中)
本文接着上一篇文章<LeetCode刷题总结-数组篇(上)>,继续讲第二个常考问题:矩阵问题. 矩阵也可以称为二维数组.在LeetCode相关习题中,作者总结发现主要考点有:矩阵元素的遍历 ...
- LeetCode刷题总结-数组篇(下)
本期讲O(n)类型问题,共14题.3道简单题,9道中等题,2道困难题.数组篇共归纳总结了50题,本篇是数组篇的最后一篇.其他三个篇章可参考: LeetCode刷题总结-数组篇(上),子数组问题(共17 ...
- Java线上问题排查神器Arthas快速上手与原理浅谈
前言 当你兴冲冲地开始运行自己的Java项目时,你是否遇到过如下问题: 程序在稳定运行了,可是实现的功能点了没反应. 为了修复Bug而上线的新版本,上线后发现Bug依然在,却想不通哪里有问题? 想到可 ...
- 快速上手RaphaelJS-Instant RaphaelJS Starter翻译(一)
(目前发现一些文章被盗用的情况,我们将在每篇文章前面添加原文地址,本文源地址:http://www.cnblogs.com/idealer3d/p/Instant_RaphaelJS_Start ...
随机推荐
- Javascript的重要数据类型-对象
这次的分享,主要还是想跟大家聊聊Javascript语言中很重要的概念之一,对象.为什么说之一呢?因为Javascript其他重要概念还包括:作用域 作用域链 继承 闭包 函数 继承 数组 ..... ...
- Typecho的卡哇伊小猫咪小插件(Live2D猫咪插件)
之前看到一个博客,被它博客上的动态小猫咪给吸引了,这个纯粹就是一个在线撸猫的神器啊.但是在网上寻找一番,并没有找到合适的插件,或者说没有找到合适的模型,因此无奈之后,只能向该博主请教,在它写的博客上, ...
- linux学习之编译-链接
在Windows下使用习惯了IDE,导致我们对程序的编译链接没有一个清晰的认识,甚至混淆了编辑器和编译器的概念.在学习Linux时,这些问题就暴露出来了. 实际上,我们应该严格区分一个程序从产生到执行 ...
- MySQL进阶之存储引擎MyISAM与InnoDB的区别
一.存储引擎(表类型) 通常意义上,数据库就是数据的集合,具体到计算机数据库可以是存储器上一些文件的集合或一些内存数据的集合.我们通常说的MySQL数据库.sql Server数据库等其实是数据库管理 ...
- php 时间 日期
获取月初与月末 /** * 获取当前月初与月末时间 * * */ $month =8; $year = 2019; $startDay = $year . '-' . $month . '-1'; $ ...
- 【vue 权威指南】 学习笔记 一
内容简介 vue.js 是一个用来开发Web界面的前端库. 1.vue.js 是什么 vue.js 是一个构建数据驱动的web界面的库,vue.js 通过简单的API提供高效的数据绑定和灵活的组件系统 ...
- PHP实现导出Excel文件
实现代码: private function exportExcel($params) { $arr = $this->getExportData($params); // 获取需要导出的信息( ...
- linux - mysql 异常:ERROR 1820 (HY000): You must SET PASSWORD before executing this statement
问题描述 ERROR 1820 (HY000): You must SET PASSWORD before executing this statement 备注:新安装完数据库后,在 xshell ...
- Java基本语法--控制台输入(Scanner类)
通过Scanner类获取用户输入时,控制台会一直等待用户的输入,可以输入不同类型的值.本篇博客主要讲解从控制台输入值,即Scanner类的使用方法. Api文档中关于Scanner类的构造方法 键盘输 ...
- Fragment应用
使用母页和子页配合展示内容:母页和子页都有自己的activity. 母页是含有frameLayout控件的页面.子页通过配置,在frameLayout控件中显示:frameLayout本身没有任何内容 ...