【LeetCode题解】动态规划:从新手到专家(一)
文章标题借用了Hawstein的译文《动态规划:从新手到专家》。
1. 概述
动态规划( Dynamic Programming, DP)是最优化问题的一种解决方法,本质上状态空间的状态转移。所谓状态转移是指每个阶段的最优状态(对应于子问题的解)可以从之前的某一个或几个阶段的状态中得到,这个性质叫做最优子结构。而不管之前这个状态是如何得到的,这被称之为无后效性。
DP问题中最经典的莫过于01背包问题:
有N件物品和一个容量为V的背包。第i件物品的费用是c[i],价值是w[i]。求解将哪些物品装入背包可使价值总和最大。
用子问题定义状态:即f[i][v]表示前i件物品恰放入一个容量为v的背包可以获得的最大价值;则其状态转移方程:
f[i][v]=max{f[i-1][v],f[i-1][v-c[i]]+w[i]}
“将前i件物品放入容量为v的背包中”这个子问题,若只考虑第i件物品的策略(放或不放),那么就可以转化为一个只牵扯前i-1件物品的问题。如果不放第i件物品,那么问题就转化为“前i-1件物品放入容量为v的背包中”,价值为f[i-1][v];如果放第i件物品,那么问题就转化为“前i-1件物品放入剩下的容量为v-c[i]的背包中”,此时能获得的最大价值就是f[i-1][v-c[i]]再加上通过放入第i件物品获得的价值w[i]。
2. 题解
| LeetCode题目 | 归类 |
|---|---|
| 53. Maximum Subarray | 子数组最大和 |
| 121. Best Time to Buy and Sell Stock | 子数组最大和 |
| 122. Best Time to Buy and Sell Stock II | 子序列最大和 |
| 123. Best Time to Buy and Sell Stock III | |
| 188. Best Time to Buy and Sell Stock IV | |
| 55. Jump Game | |
| 70. Climbing Stairs | |
| 62. Unique Paths | |
| 63. Unique Paths II | |
| 64. Minimum Path Sum | 最短路径 |
| 91. Decode Ways |
以下代码既有Java,也有Go。
53. Maximum Subarray
子数组最大和问题,求解方法可用Kadane算法。
121. Best Time to Buy and Sell Stock
题目大意:给定数组\(a[..]\),求解\(\max a[j] - a[i] \quad j > i\)。
解决思路:将数组a的相邻值相减(右边减左边)变换成数组b,上述问题转变成了求数组b的子数组最大和问题.
// Kadane algorithm to solve Maximum subArray problem
public int maxProfit(int[] prices) {
int maxEndingHere = 0, maxSoFar = 0;
for (int i = 1; i < prices.length; i++) {
maxEndingHere += prices[i] - prices[i - 1];
maxEndingHere = Math.max(maxEndingHere, 0);
maxSoFar = Math.max(maxEndingHere, maxSoFar);
}
return maxSoFar;
}
122. Best Time to Buy and Sell Stock II
之前问题Best Time to Buy and Sell Stock的升级版,对交易次数没有限制,相当于求解相邻相减后形成的子序列最大和——只要为正数,则应计算在子序列内。
public int maxProfit(int[] prices) {
int max = 0;
for (int i = 1; i < prices.length; i++) {
if (prices[i] > prices[i - 1]) {
max += (prices[i] - prices[i - 1]);
}
}
return max;
}
123. Best Time to Buy and Sell Stock III
最多允许交易两次。
public int maxProfit(int[] prices) {
int sell1 = 0, sell2 = 0;
int buy1 = Integer.MIN_VALUE, buy2 = Integer.MIN_VALUE;
for (int price : prices) {
buy1 = Math.max(buy1, -price); // borrow
sell1 = Math.max(sell1, buy1 + price);
buy2 = Math.max(buy2, sell1 - price);
sell2 = Math.max(sell2, buy2 + price);
}
return sell2;
}
188. Best Time to Buy and Sell Stock IV
最多允许交易k次。当k >= n/2时,在任意时刻都可以进行交易(一次交易包括买、卖),因此该问题退化为了问题122. Best Time to Buy and Sell Stock II。其他情况则有递推式:
\]
其中,\(c_{i,j}\)表示在\(t\)时刻共\(i\)次交易产生的最大收益。
public int maxProfit(int k, int[] prices) {
int n = prices.length;
if (n <= 1) {
return 0;
}
// make transaction at any time
else if (k >= n / 2) {
return maxProfit122(prices);
}
int[][] c = new int[k + 1][n];
for (int i = 1; i <= k; i++) {
int localMax = -prices[0];
for (int j = 1; j < n; j++) {
c[i][j] = Math.max(c[i][j - 1], localMax + prices[j]);
localMax = Math.max(localMax, c[i - 1][j] - prices[j]);
}
}
return c[k][n - 1];
}
public int maxProfit122(int[] prices) {
int max = 0;
for (int i = 1; i < prices.length; i++) {
if (prices[i] > prices[i - 1]) {
max += (prices[i] - prices[i - 1]);
}
}
return max;
}
55. Jump Game
限制当前最大跳跃数,问是否能到达最后一个index。需要反向往后推演。
public boolean canJump(int[] nums) {
int n = nums.length, index = n - 1;
for (int i = n - 2; i >= 0; i--) {
if (i + nums[i] >= index)
index = i;
}
return index <= 0;
}
70. Climbing Stairs
题目大意:每一次可以加1或加2,那么从0加到n共有几种加法?
假定\(d_i\)表示加到i的种数,那么就有递推式\(d_i = d_{i-1} + d_{i-2}\)。
func climbStairs(n int) int {
if(n < 1) {
return 0;
}
d := make([]int, n+1)
d[1] = 1
if n >= 2 {
d[2] = 2
}
for i := 3; i<=n; i++ {
d[i] = d[i-1] + d[i-2]
}
return d[n]
}
62. Unique Paths
题目大意:求解从左上角到右下角的路径数。
路径数递推式:\(c_{i,j}= c_{i-1,j} + c_{i,j-1}\)。
func uniquePaths(m int, n int) int {
f := make([][]int, m)
for i := range f {
f[i] = make([]int, n)
}
// handle boundary condition: f[][0] and f[0][]
f[0][0] = 1
for i := 1; i < m; i++ {
f[i][0] = 1
}
for j := 1; j < n; j++ {
f[0][j] = 1
}
for i := 1; i < m; i++ {
for j := 1; j < n; j++ {
f[i][j] = f[i][j - 1] + f[i - 1][j]
}
}
return f[m-1][n-1]
}
63. Unique Paths II
加了限制条件,有的点为obstacle——不允许通过。上面的递推式依然成立,只不过要加判断条件。另外,在实现过程中可以用一维数组代替二维数组,比如说按行或按列计算。
public int uniquePathsWithObstacles(int[][] obstacleGrid) {
int columnSize = obstacleGrid[0].length;
int[] c = new int[columnSize];
c[0] = 1;
for (int[] row : obstacleGrid) {
for (int j = 0; j < columnSize; j++) {
if (row[j] == 1)
c[j] = 0;
else if (j >= 1)
c[j] += c[j - 1];
}
}
return c[columnSize - 1];
}
64. Minimum Path Sum
题目大意:从矩阵的左上角到右下角的最短路径。
加权路径值\(c_{i,j}= \max (c_{i-1,j},c_{i,j-1}) + w_{i,j}\),其中,\(w_{i,j}\)为图中边的权值。
// the shortest path for complete directed graph
func minPathSum(grid [][]int) int {
var m, n = len(grid), len(grid[0])
f := make([][]int, m)
for i := range f {
f[i] = make([]int, n)
}
// handle boundary condition: f[][0] and f[0][]
f[0][0] = grid[0][0]
for i := 1; i < m; i++ {
f[i][0] = f[i - 1][0] + grid[i][0]
}
for j := 1; j < n; j++ {
f[0][j] = f[0][j-1] + grid[0][j]
}
for i :=1; i < m; i++ {
for j := 1; j<n; j++ {
if(f[i-1][j] < f[i][j-1]) {
f[i][j] = f[i-1][j] + grid[i][j]
} else {
f[i][j] = f[i][j-1] + grid[i][j]
}
}
}
return f[m-1][n-1]
}
91. Decode Ways
求解共有多少种解码情况。
public int numDecodings(String s) {
int n = s.length();
if (n == 0 || (n == 1 && s.charAt(0) == '0'))
return 0;
int[] d = new int[n+1];
d[n] = 1;
d[n - 1] = s.charAt(n - 1) == '0' ? 0 : 1;
for (int i = n-2; i >= 0; i--) {
if(s.charAt(i) == '0')
continue;
else if(Integer.parseInt(s.substring(i, i+2)) <= 26)
d[i] += d[i + 2];
d[i] += d[i + 1];
}
return d[0];
}
【LeetCode题解】动态规划:从新手到专家(一)的更多相关文章
- [LeetCode 题解]:Best Time to Buy and Sell Stock
前言 [LeetCode 题解]系列传送门: http://www.cnblogs.com/double-win/category/573499.html 1.题目描述 Say you ha ...
- Leetcode之动态规划(DP)专题-486. 预测赢家(Predict the Winner)
Leetcode之动态规划(DP)专题-486. 预测赢家(Predict the Winner) 给定一个表示分数的非负整数数组. 玩家1从数组任意一端拿取一个分数,随后玩家2继续从剩余数组任意一端 ...
- Leetcode之动态规划(DP)专题-877. 石子游戏(Stone Game)
Leetcode之动态规划(DP)专题-877. 石子游戏(Stone Game) 亚历克斯和李用几堆石子在做游戏.偶数堆石子排成一行,每堆都有正整数颗石子 piles[i] . 游戏以谁手中的石子最 ...
- LeetCode题解分类汇总(包括剑指Offer和程序员面试金典,持续更新)
LeetCode题解汇总(持续更新,并将逐步迁移到本博客列表中) 剑指Offer 数据结构 链表 序号 题目 难度 06 从尾到头打印链表 简单 18 删除链表的节点 简单 22 链表中倒数第k个节点 ...
- 【LeetCode题解】二叉树的遍历
我准备开始一个新系列[LeetCode题解],用来记录刷LeetCode题,顺便复习一下数据结构与算法. 1. 二叉树 二叉树(binary tree)是一种极为普遍的数据结构,树的每一个节点最多只有 ...
- LeetCode之“动态规划”:Distinct Subsequences
题目链接 题目要求: Given a string S and a string T, count the number of distinct subsequences of T in S. A s ...
- leetcode题解-122买卖股票的最佳时期
题目 leetcode题解-122.买卖股票的最佳时机:https://www.yanbinghu.com/2019/03/14/30893.html 题目详情 给定一个数组,它的第 i 个元素是一支 ...
- 【LeetCode题解】3_无重复字符的最长子串(Longest-Substring-Without-Repeating-Characters)
目录 描述 解法一:暴力枚举法(Time Limit Exceeded) 思路 Java 实现 Python 实现 复杂度分析 解法二:滑动窗口(双指针) 思路 Java 实现 Python 实现 复 ...
- 【LeetCode题解】225_用队列实现栈(Implement-Stack-using-Queues)
目录 描述 解法一:双队列,入快出慢 思路 入栈(push) 出栈(pop) 查看栈顶元素(peek) 是否为空(empty) Java 实现 Python 实现 解法二:双队列,入慢出快 思路 入栈 ...
随机推荐
- Linux_Ununtu 16.04 的下载安装并部署.Net Core 网站
第一次接触Linux也难免有些懵逼,因为公司项目必须用.Net Core 开发一个后端服务应用:第一次用Linux给我的感觉就像在用2000年的手机一样:没用智能的操作:让人崩溃的用户体验.说多了都是 ...
- 将数据库从服务器移到浏览器--indexedDB基本操作封装
数据库是属于服务器的,这是天经地义的事,但是有时候数据也许并非需要存储在服务器,但是这些数据也是一条一条的记录,怎么办?今天来带领你领略一下H5的一个新特性--indexedDB的风骚.你会情不自禁的 ...
- thinkphp5.0学习笔记(四)数据库的操作
ThinkPHP内置了抽象数据库访问层,把不同的数据库操作封装起来,我们只需要使用公共的Db类进行操作,而无需针对不同的数据库写不同的代码和底层实现,Db类会自动调用相应的数据库驱动来处理.采用PDO ...
- 【DG】[三思笔记]一步一步学DataGuard
[DG][三思笔记]一步一步学DataGuard 它有无数个名字,有人叫它dg,有人叫它数据卫士,有人叫它data guard,在oracle的各项特性中它有着举足轻理的地位,它就是(掌声)..... ...
- ASP.NET Core部署到CentOS7,使用Nginx代理
ASP.NET Core 的运行环境由新开发的 Kestrel Server 负责,IIS 退回到 HTTP 的侦听器的角色,微软也特别为了这个需求开发了 IIS Platform Handler,以 ...
- 怀念Galois
我的第一篇谈到具体学科的博客,还是献给我最钟爱的数学. 个人比较喜欢离散数学,并非因为曲高和寡,而是因为数学分析.概率论.拓扑学.泛函之类的高手实在太多.而离散数学更为抽象,抽象到抽象代数直接以抽象二 ...
- jeecg关闭当前iframe
关闭当前iframe function closeDialog(){ frameElement.api.close();//本方法也行 //或者下面的方式 var win = frameElement ...
- java高并发锁的3种实现
初级技巧 - 乐观锁 乐观锁适合这样的场景:读不会冲突,写会冲突.同时读的频率远大于写. 以下面的代码为例,悲观锁的实现: Java代码 public Object get(Object key) ...
- mysql 修改表结构的字段名
alter table domains change STATUS status tinyint(1) not null;
- [leetcode-312-Burst Balloons]
Given n balloons, indexed from 0 to n-1. Each balloon is painted with a number on it represented by ...