LeeCode 动态规划(一)
简述
如果某一问题存在很多重叠子问题,使用动态规划是非常有效的。
动态规划与贪心
- 贪心:每次都选择局部最优解
- 动态规划:每个状态都是由前一个状态推导得到
动态规划解题步骤
- 确定
dp数组
及下标的含义 - 确定递推公式
dp数组
初始化- 确定遍历顺序
LeeCode 509:斐波那契数
题目描述
斐波那契数(通常使用
F(n)
表示)形成的序列称为斐波那契数列。该数列由0
和1
开始,后面的每一项数字都是前面两项数字的和。即 \(F(0) = 0;F(1) = 1; F(n) = F(n - 1) + F(n - 2) \quad n > 1\)。给定n
,请计算F(n)
。
建立模型
- 确定dp数组及下标的含义, 这里省略了dp数组(当前状态只和前两个状态有关), 借用两个参数a, b来代替. 数组的含义为 F(i)
- 确定递推公式, dp[i] = dp[i - 1] + dp[i - 2] 即 res = a + b
- dp数组初始化, dp[0] = 0, dp[1] = 1 即 a = 0, b = 1
- 确定遍历顺序, 2 ~ n顺序遍历
代码实现
public int fib(int n) {
if (n <= 1) {
return n;
}
int a = 0, b = 1;
int res = 1;
for (int i = 2; i <= n; i++) {
res = a + b;
a = b;
b = res;
}
return res;
}
LeeCode 70:爬楼梯
题目描述
假设你正在爬楼梯,需要
n
阶你才能到达楼顶。每次你可以爬1
或2
个台阶。你有多少种不同的方法可以爬到楼顶呢?
建立模型
此题与斐波那契数列属于同一题, 不过此次笔者没有省略 dp数组.
- 确定dp数组及下标的含义, 数组的含义为爬到第 i 阶楼梯的方法数
- 确定递推公式
dp[i] = dp[i - 1] + dp[i - 2]
- dp数组初始化,
dp[1] = 1, dp[2] = 2
- 确定遍历顺序, 3~n 顺序遍历
代码实现
public int climbStairs(int n) {
if (n <= 2) {
return n;
}
int[] dp = new int[n + 1];
// dp数组初始化
dp[1] = 1;
dp[2] = 2;
for (int i = 3; i < dp.length; i++) {
dp[i] = dp[i - 1] + dp[i - 2];
}
return dp[n];
}
LeeCode 746:使用最小花费爬楼梯
题目描述
给你一个整数数组
cost
,其中cost[i]
是从第i
个台阶向上爬需要支付的费用。一旦你支付此费用,即可选择向上爬一个或者两个台阶。你可以选择从下标为
0
或 下标为1
的台阶开始爬楼梯。请你计算并返回达到楼梯顶部的最低花费。
建立模型
- 确定dp数组及下标的含义, 数组的含义为到达第 i 个台阶的最小花费
- 确定递推公式
dp[i] = Math.min(dp[i - 1] + dp[i - 2]) + cost[i]
- 初始化dp数组,
dp[0] = cost[0], dp[1] = cost[1]
- 确定遍历顺序, 从 2~n 顺序遍历
代码实现
public int minCostClimbingStairs(int[] cost) {
if (cost.length == 2) {
return Math.min(cost[0], cost[1]);
}
int[] dp = new int[cost.length];
dp[0] = cost[0];
dp[1] = cost[1];
for (int i = 2; i < dp.length; i++) {
/**
* 递推数组
* 爬到第 i 阶楼梯的方式包括 从 i - 1 阶爬一个台阶 和 从 i - 2 阶爬两个台阶
* 取其中的花费较小者
*/
dp[i] = Math.min(dp[i - 1], dp[i - 2]) + cost[i];
}
/**
* 到达顶部的花费选择 倒数第一个台阶和倒数第二个台阶的较小者
*/
return Math.min(dp[cost.length - 1], dp[cost.length - 2]);
}
LeeCode 62:不同路径
题目描述
一个机器人位于一个
m x n
网格的左上角 (起始点在下图中标记为 “Start” )。机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。
问总共有多少条不同的路径?
建立模型
- 确定dp数组及下标含义, 数组的含义为到达第i行第j列的路径总数
- 确定递推公式
dp[i][j] = dp[i - 1][j] + dp[i][j - 1]
- 初始化dp数组, 第0行和第0列全都初始化为1
- 确定遍历顺序, 外循环从左至右, 内循环从上至下
代码实现
public int uniquePaths(int m, int n) {
int[][] dp = new int[m][n];
// dp数组初始化
// 第 0 行初始化
for (int i = 0; i < n; i++) {
dp[0][i] = 1;
}
// 第 0 列初始化
for (int j = 0; j < m; j++) {
dp[j][0] = 1;
}
for (int i = 1; i < m; i++) {
for (int j = 1; j < n; j++) {
dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
}
}
return dp[m - 1][n - 1];
}
LeeCode 63:不同路径II
题目描述
一个机器人位于一个
m x n
网格的左上角 (起始点在下图中标记为 “Start” )。机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish”)。
现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径?
网格中的障碍物和空位置分别用 1 和 0 来表示。
建立模型
- 确定dp数组及下标的含义, 数组的含义为到达第i行第j列的路径总数
- 确定递推公式
dp[i][j] = dp[i - 1][j] + dp[i][j - 1]
, 对于网格中的障碍物有dp[i][j] = 0
- 初始化dp数组,
dp[0][0]
根据是否有障碍物初始化为0或1, 对于第0行, 则根据左边是否有障碍物初始化为0或1, 第0列则根据上边是否有障碍无初始化为0或1 - 确定遍历顺序, 外循环从左至右, 内循环从上至下
代码实现
public int uniquePathsWithObstacles(int[][] obstacleGrid) {
int m = obstacleGrid.length;
int n = obstacleGrid[0].length;
int[][] dp = new int[m][n];
// dp数组初始化
if (obstacleGrid[0][0] == 1) {
dp[0][0] = 0;
}
else {
dp[0][0] = 1;
}
// 第 0 行初始化
for (int i = 1; i < n; i++) {
if (obstacleGrid[0][i] == 1) {
dp[0][i] = 0;
}
else {
dp[0][i] = dp[0][i - 1];
}
}
// 第 0 列初始化
for (int j = 1; j < m; j++) {
if (obstacleGrid[j][0] == 1) {
dp[j][0] = 0;
}
else {
dp[j][0] = dp[j - 1][0];
}
}
// 遍历dp数组
for (int i = 1; i < m; i++) {
for (int j = 1; j < n; j++) {
if (obstacleGrid[i][j] == 1) {
dp[i][j] = 0;
}
else {
dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
}
}
}
return dp[m - 1][n - 1];
}
LeeCode 343:整数拆分
题目描述
给定一个正整数
n
,将其拆分为k
个 正整数 的和(k >= 2
),并使这些整数的乘积最大化。返回 你可以获得的最大乘积 。
建立模型
- 确定dp数组及下标的含义, 数组的含义为 i 可拆分成的乘积最大值
- 确定递推公式
dp[i] = MAX(Math.max(j * (i - j), j * dp[i - j])) (1 <= j < i)
- 初始化dp数组, 全都初始化为0
- 确定遍历顺序,
i -> 2~n, j -> 1~(i-1)
代码实现
public int integerBreak(int n) {
int[] dp = new int[n + 1];
for (int i = 2; i <= n; i++) {
int max_value = 0;
for (int j = 1; j < i; j++) {
/**
* j * (i - j) 表示 (i - j) 不继续拆分的乘积值
* j * dp[i - j] 表示 (i - j) 继续拆分的乘积值
*/
max_value = Math.max(max_value, Math.max(j * (i - j), j * dp[i - j]));
}
dp[i] = max_value;
}
return dp[n];
}
LeeCode 96:不同的二叉搜索树
题目描述
给你一个整数
n
,求恰由n
个节点组成且节点值从1
到n
互不相同的 二叉搜索树 有多少种?返回满足题意的二叉搜索树的种数。
建立模型
确实dp数组及其下标的含义, 数组的含义为 i 个节点能构成的不同的二叉搜索树
初始化dp数组,
dp[0] = dp[1] = 1
, 空树或单节点的树只有一种确定递推公式,
dp[i] += dp[j] * dp[i - j - 1]
, 该公式的意思为依次遍历每个数字将其作为根节点, 将
1~j-1
作为左子树,j+1~i
作为右子树, 同时左右子树也需要满足是二叉搜索树, 且由于根值不同, 可以保证每棵二叉搜索树都是不同的, 所以
该问题就转化成了两个规模更小的问题。
确定遍历顺序,
i -> 2~n, j -> 1~i
代码实现
public int numTrees(int n) {
int[] dp = new int[n + 1];
// dp数组初始化
dp[0] = 1;
dp[1] = 1;
// 遍历数组
for (int i = 2; i <= n; i++) {
for (int j = 1; j <= i; j++) {
dp[i] += dp[j - 1] * dp[i - j];
}
}
return dp[n];
}
LeeCode 动态规划(一)的更多相关文章
- 好像leeceode题目我的博客太长了,需要重新建立一个. leecode刷题第二个
376. Wiggle Subsequence 自己没想出来,看了别人的分析. 主要是要分析出升序降序只跟临近的2个决定.虽然直觉上不是这样. 455. 分发饼干 ...
- 算法题思路总结和leecode继续历程
2018-05-03 刷了牛客网的题目:总结思路(总的思路跟数学一样就是化简和转化) 具体启发点: 1.对数据进行预处理排序的思想:比如8皇后问题 2.对一个数组元素进行比较的操作,如果复杂,可以试试 ...
- 增强学习(三)----- MDP的动态规划解法
上一篇我们已经说到了,增强学习的目的就是求解马尔可夫决策过程(MDP)的最优策略,使其在任意初始状态下,都能获得最大的Vπ值.(本文不考虑非马尔可夫环境和不完全可观测马尔可夫决策过程(POMDP)中的 ...
- 简单动态规划-LeetCode198
题目:House Robber You are a professional robber planning to rob houses along a street. Each house has ...
- leecode系列--Two Sum
学习这件事在任何时间都不能停下.准备坚持刷leecode来提高自己,也会把自己的解答过程记录下来,希望能进步. Two Sum Given an array of integers, return i ...
- 动态规划 Dynamic Programming
March 26, 2013 作者:Hawstein 出处:http://hawstein.com/posts/dp-novice-to-advanced.html 声明:本文采用以下协议进行授权: ...
- 动态规划之最长公共子序列(LCS)
转自:http://segmentfault.com/blog/exploring/ LCS 问题描述 定义: 一个数列 S,如果分别是两个或多个已知数列的子序列,且是所有符合此条件序列中最长的,则 ...
- C#动态规划查找两个字符串最大子串
//动态规划查找两个字符串最大子串 public static string lcs(string word1, string word2) { ...
- C#递归、动态规划计算斐波那契数列
//递归 public static long recurFib(int num) { if (num < 2) ...
- 动态规划求最长公共子序列(Longest Common Subsequence, LCS)
1. 问题描述 子串应该比较好理解,至于什么是子序列,这里给出一个例子:有两个母串 cnblogs belong 比如序列bo, bg, lg在母串cnblogs与belong中都出现过并且出现顺序与 ...
随机推荐
- 升级openssl版本
一.安装步骤 1.下载openssl安装包 2.编译安装 3.备份旧版本openssl 4.添加软连接 5.添加OpenSSL动态链接库并使其生效 二.下载openssl安装包 [root@local ...
- linux查看所有的用户和组信息
1.cat /etc/passwd 查看所有用户 2.cat /etc/passwd|grep 用户名,用于查找某个用户 3.cat /etc/group查看所有组信息 4.cat /etc/g ...
- javaweb本地启动很快,服务器上面启动特别慢
在JVM环境中解决 打开$JAVA_PATH/jre/lib/security/java.security这个文件,找到下面的内容: securerandom.source=file:/dev/ura ...
- Zookeeper分布式服务
Zookeeper(CP) 以集群的方式[leader和follower]为分布式应用提供协调服务.负责存储和管理大家都关系的数据,接受观察者注册.消息分发等服务 特点: 只要有半数以上的节点存活就能 ...
- (五).JavaScript的函数
1. 函数 1.1 函数基础简介 函数介绍 函数:具有特定功能的代码块 本质:一种对象数据类型 功能:1. 代码复用 2. 项目模块化 函数组成(两者必须同时存在): 1. 函数定义 2. 函数调用 ...
- PCRaster安装
改了很久才import成功.期间查了不少东西,虽然大部分没用上,但还是记录一下. PCRaster的安装和个人的最终解决方法 Software for environmental modelling ...
- python学习记录(一)-基础
交换变量值 a,b = 10,20 print(a,b) #10 20 a,b = b,a print(a,b) #20 10 大字符串 str = '''最近在看的动漫: 黑之契约者.咒术回战... ...
- jenkins启动失败,查看状态提示active(exited)
chown -R jenkins:jenkins /var/lib/jenkins chown -R jenkins:jenkins /var/cache/jenkins chown -R jenki ...
- WinForm中的MVC模式--MVP模式
本文主要介绍MVC模式在WINFORM中的实现,其实砖家们都称它为MVP模式,小弟E文不太好,真的是记不住那个P怎么拼写的.. MVC模式主要解决的问题就是将表示层和业务层进行分离,在以往做WINFO ...
- Codeforces Round #803 (Div. 2) A-D 刚vp完还没补题
Codeforces Round #803 (Div. 2) 2022/7/24 上午VP 传送门:https://codeforces.com/contest/1698 A. XOR Mixup 随 ...