leetcode 动态规划(DP)

本次题解包括

  • 53. Maximum Subarray
  • 62. Unique Paths
  • 63. Unique Paths II
  • 64. Minimum Path Sum
  • 70. Climbing Stairs
  • 72. Edit Distance
  • 87. Scramble String
  • 91 . Decode Ways
  • 97. Interleaving String
  • 115 Distinct Subsequences
  • 120. Triangle
  • 139. Word Break
  • 140. Word Break II
  • 152. Maximum Product Subarray
  • 174 . Dungeon Game
  • 198. House Robber
  • 213 . House Robber II
  • 221 . Maximal Square
  • 712. Minimum ASCII Delete Sum for Two Strings
  • 718. Maximum Length of Repeated Subarray
  • 799. Champagne Tower
  • 818. Race Car

53. Maximum Subarray

Find the contiguous subarray within an array (containing at least one number) which has the largest sum.

For example, given the array [-2,1,-3,4,-1,2,1,-5,4],
the contiguous subarray [4,-1,2,1] has the largest sum = 6.

More practice:

If you have figured out the O(n) solution, try coding another solution using the divide and conquer approach, which is more subtle.

题目地址:leetcode Maximum Subarray

题目大意:给定一串数字,要求求出连续的序列,使得这个连续序列的和最大

水水的DP~

当前和为sum,如果sum >0,那么加上当前元素,否则sum=A[i] (即抛弃负数的sum,重新开始。因为负数的sum是累赘- -好难听的样子)

C++

Python

 

分治法

《编程珠玑》里其实有讲过。 最大值要么在左边要么在中间,还有就是中间的情况

左右两边可以递归没什么好说的。

中间的就是从mid到left找最大的left_sum,以及从mid+1到right找最大的right_sum,就是left_sum + right_sum

C++

 

 


 

62. Unique Paths

A robot is located at the top-left corner of a m x n grid (marked ‘Start’ in the diagram below).

The robot can only move either down or right at any point in time. The robot is trying to reach the bottom-right corner of the grid (marked ‘Finish’ in the diagram below).

How many possible unique paths are there?

Above is a 3 x 7 grid. How many possible unique paths are there?

Note:m and n will be at most 100.

题目地址:leetcode Unique Paths

题目大意: :从m*n大小的图中左上方走到右下方,每次只能向右或者向下,问一共有多少种走法

思路:

  1. 设dp[i][j]为到坐标为(i,j)的方法数,则有dp[i][j]= dp[i-1][j]+dp[i][j-1] 水~
  2. 这是看了discuss里面,有人用数学方法做的。 orz 原文如下:
  • It’s true that this can be solved with dynamic programming. But you can see that every path has exactly m – 1 horizontal moves and n – 1 vertical moves. So, to get a particular path, you need to choose where to put your m – 1 horizontal moves (or your n – 1 vertical moves) amongst the m + n – 2 total moves. That gives (m+n-2 choose m-1) paths (or (m+n-2 choose n-1), which is the same).

C++

Python方法1

Python 方法2

 


 

63. Unique Paths II

Follow up for “Unique Paths”:

Now consider if some obstacles are added to the grids. How many unique paths would there be?

An obstacle and empty space is marked as 1 and 0 respectively in the grid.

For example,

There is one obstacle in the middle of a 3×3 grid as illustrated below.

The total number of unique paths is 2.

Note:m and n will be at most 100.

题目地址:leetcode Unique Paths II

题目大意: 和上面一题差不多,都是要求从左上角到右下角的方法数,只不过这题有障碍物,有障碍物的不能走。

思路:还是dp,就稍微改下即可。 设dp[i][j]为到坐标为(i,j)的方法数。对于(i,j):

  • (i,j)为障碍物,则dp[i][j]=0
  • (i,j)不为障碍物则 dp[i][j]= dp[i-1][j]+dp[i][j-1]

C++

 

Python

 

 


 

64. Minimum Path Sum

Given a m x n grid filled with non-negative numbers, find a path from top left to bottom right which minimizes the sum of all numbers along its path.

Note: You can only move either down or right at any point in time.

题目地址:leetcode Minimum Path Sum

题目大意: m*n的格子中有m*n个非负整数,求从左上角到右下角中,经过的数字和的最小值(每次只能向右或者向下走)

 

思路:水题,其实和上面的也差不多。继续dp。

设dp[i][j]为当前到达坐标为(i,j)的最小和,dp[i][j]=min(dp[i – 1][j], dp[i][j – 1]) + grid[i ][j ];

我的版本由于dp数组和grid坐标差了1所以为min(dp[i – 1][j], dp[i][j – 1]) + grid[i – 1][j – 1];

其实也可以O(N)的空间实现的。

C++

版本2

Python

 


 

70. Climbing Stairs

You are climbing a stair case. It takes n steps to reach to the top.
Each time you can either climb 1 or 2 steps. In how many distinct ways can you climb to the top?
Note: Given n will be a positive integer.

题目地址:leetcode Climbing Stairs

题目大意: 爬楼梯,每次可以爬一步或者两步,求从1爬到n的方法数

思路:水~  dp[i] = dp[i – 1] + dp[i – 2];

但只需三个变量即可。

C++

Python

还可以用矩阵快速幂的方法:

设Q^n如下:
$$Q^n = \left[
\begin{matrix}
f_{n+1} & f_{n} \\
f_{n} & f_{n-1} \\
\end{matrix}
\right]
\\
初始值Q^1=
\left[
\begin{matrix}
1 &1 \\
1 & 0 \\
\end{matrix}
\right]
\\$$

可以用数学归纳法证明:

$$\begin{alignat}{2}Q^k &= \left[
\begin{matrix}
f_{k+1} & f_{k} \\
f_{k} & f_{k-1} \\
\end{matrix}
\right]
\end{alignat}$$

$$\begin{alignat}{2}Q^{k+1} &=
\left[
\begin{matrix}
f_{k+1} & f_{k} \\
f_{k} & f_{k-1} \\
\end{matrix}
\right]
\left[
\begin{matrix}
1 &1 \\
1 & 0 \\
\end{matrix}
\right]
\\& =
\left[
\begin{matrix}
f_{k+1}+f_{k} &f_{k+1} \\
f_{k+1} & f_{k} \\
\end{matrix}
\right]
\\& =
\left[
\begin{matrix}
f_{k+2} &f_{k+1} \\
f_{k+1} & f_{k} \\
\end{matrix}
\right]
\end{alignat}$$

C++ 复杂度O(Logn)

 

 

 

72. Edit Distance

Given two words word1 and word2, find the minimum number of steps required to convert word1 to word2. (each operation is counted as 1 step.)

You have the following 3 operations permitted on a word:

a) Insert a character
b) Delete a character
c) Replace a character

题目地址:leetcode Edit Distance

题目大意:给定两个字符串,求他们的编辑距离(即可以插入、修改、删除一个字符,用最少的步数使得一个字符串变为另一个字符串)

思路:设dp[i][j]为以i和j结尾的字符串的编辑距离。

  • if word[i] == word[j] : dp[i][j] = dp[i-1][j-1]
  • if word[i] != word[j] : dp[i][j]  下面三个数的最小值+1:
    • dp[i-1][j]   可以看为word1[i-1]然后插入一个字符(word2[j]),或者说删掉 word2[j]
    • dp[i][j-1]   可以看为word2[j-1]然后插入一个数(word1[i]),或者说删掉 word1[i]
    • dp[i-1][j-1] 把word1[i] 改为word2[j],或者word2[j]改为word1[i]

还可以只用O(n)空间, 见java版本。其他语言其实类似,不想写了。

C++

Python

 

Java

因为只用到左上方,左边,和上边的元素,所以可以只用两个数组。

 

 


 

87. Scramble String

Given a string s1, we may represent it as a binary tree by partitioning it to two non-empty substrings recursively.

Below is one possible representation of s1 = "great":

To scramble the string, we may choose any non-leaf node and swap its two children.

For example, if we choose the node "gr" and swap its two children, it produces a scrambled string "rgeat".

We say that "rgeat" is a scrambled string of "great".

Similarly, if we continue to swap the children of nodes "eat" and "at", it produces a scrambled string "rgtae".

We say that "rgtae" is a scrambled string of "great".

Given two strings s1 and s2 of the same length, determine if s2 is a scrambled string of s1.

题目地址:leetcode Scramble String

题目大意:给定两个字符串s1和s2,判断s2是否是s1经过旋转得到的。

思路:

根据题目的描述,我们可以枚举切分点i,然后递归判断:

  • s1[0, i-1]和s2[0, i-1] 是否可以旋转得到,并且s1[i, n – 1]和s2[i, n- 1] 也要能旋转得到
  • 或者s1[0, i – 1]和s2[n – i, n – 1] 是否可以旋转得到,并且s1[i, n- 1]和s2[0, n – i] 也要能旋转得到

可以用记忆化搜索防止重复搜索,还可以用排序剪枝(看看当前的s1和s2排序后是否相等)

C++

Python

 


 

91. Decode Ways

A message containing letters from A-Z is being encoded to numbers using the following mapping:

‘A’ -> 1
‘B’ -> 2

‘Z’ -> 26

Given an encoded message containing digits, determine the total number of ways to decode it.

For example,
Given encoded message “12”,
it could be decoded as “AB” (1 2) or “L” (12).

The number of ways decoding “12” is 2.

题目地址:leetcode Decode Ways

题目大意:给定一个数字组成的字符串,让你看有多少种解码方式

思路:

对于一个编码后的串s,s的所有的字符出现在0~9之间。

要查看其解码方式有多少种可能,主要在于因为有的字符可以被拆分,如12可以算L也可以算AB,而这样的在10~26均是可能的。

设dp[i]为s[0…i]最多的解码方式,因此我们有:

  • 若s[i] !=’0′ ,  dp[i] += dp[i-1]
  • 若10 <= s[i-1,i] <=26 , dp[i] += dp[i-2]

C++

感觉第二次写的C++版本更清晰一些。

  • 若s[i] !=’0′ ,
    • dp[i] += dp[i-1]
    • 若10 <= s[i-1,i] <=26 , dp[i] += dp[i-2]
  • s[i] ==’0′ ,
    • 看看是否能和前面的合并。不能就返回0,可以就是dp[i-2]

 

 


 

97. Interleaving String

Given s1, s2, s3, find whether s3 is formed by the interleaving of s1 and s2.

For example,
Given:
s1 = “aabcc”,
s2 = “dbbca”,

When s3 = “aadbbcbcac”, return true.
When s3 = “aadbbbaccc”, return false.

题目地址:leetcode Interleaving String

题目大意:给定s1,s2,s3三个字符串,判断s3是否由s1和s2组成。

 

思路:设dp[i][j]为s1[i-1] , s2[j-1] 是否能组成 s3[i+j-1]

  • dp[i][j] = s3[i+j-1]==s1[i-1] && dp[i-1][j]  || s3[i+j-1]==s2[j-1] && dp[i][j-1];  (尝试用s1[i-1]去匹配,故应该是dp[i-1][j],同理用s2[j-1]去匹配)

C++

 Python

 

 

 

 

115. Distinct Subsequences

Given a string S and a string T, count the number of distinct subsequences of T in S.

A subsequence of a string is a new string which is formed from the original string by deleting some (can be none) of the characters without disturbing the relative positions of the remaining characters. (ie, "ACE" is a subsequence of "ABCDE" while "AEC" is not).

Here is an example:
S = "rabbbit", T = "rabbit"

Return 3.

题目地址:leetcode Distinct Subsequences

题目大意:给定S和T两个字符串,问把通过删除S中的某些字符,把S变为T有几种方法?

思路:DP,设dp[i][j]为到S[i] T[j]位置的方法数:

  • S[i]==T[j]:  dp[i][j] = dp[i-1][j] + dp[i-1][j-1]  两字符串相等,要么跳过不匹配,要么匹配
  • S[i]!=T[j]:  dp[i][j]= dp[i-1][j] 不相等只能不匹配这个

初始值设置:

  • dp[i][0] = dp[i – 1][0] + (s[i] == t[0])  即t[0]可以被s表示的数量

C++

另一种写法:

C++

Python

 

 


 

120. Triangle

Given a triangle, find the minimum path sum from top to bottom. Each step you may move to adjacent numbers on the row below.

For example, given the following triangle

The minimum path sum from top to bottom is 11 (i.e., 2 + 3 + 5 + 1 = 11).

Note:

  • Bonus point if you are able to do this using only O(n) extra space, where n is the total number of rows in the triangle.

题目地址:leetcode Triangle

题目大意:给定一个三角形,求其从顶部到底部最小的和。

思路:DP,可以从上往下也可以从下往上

从下往上的版本:

C++

Python

从下往上的版本:

 

 

139. Word Break

Given a string s and a dictionary of words dict, determine if s can be segmented into a space-separated sequence of one or more dictionary words.

For example, given
s = "leetcode",
dict = ["leet", "code"].

Return true because "leetcode" can be segmented as "leet code".

传送门

题意:给定一个字符串s和字典,判断字符串s是否能拆成仅由字典组成的若干个单词?

思路:dp ,设dp[i]为 [0,i-1]是否能拆分

  • dp[i+1]= true ( dp[j] =true && s[j,i]在字典中)

1A

 

 

 

 

140. Word Break II

Given a string s and a dictionary of words dict, add spaces in s to construct a sentence where each word is a valid dictionary word.

Return all such possible sentences.

For example, given
s = "catsanddog",
dict = ["cat", "cats", "and", "sand", "dog"].

A solution is ["cats and dog", "cat sand dog"].

传送门

题意:给定一个字符串s和字典,将字符串s拆成仅由字典组成的若干个单词,返回所有的解

思路:在DFS的过程中,判断接下来的过程中是否有解。(就是进行剪枝操作啦),判断是否有解类似139那题。

Python

 

 

 

 

 

152. Maximum Product Subarray

Find the contiguous subarray within an array (containing at least one number) which has the largest product.

For example, given the array [2,3,-2,4],
the contiguous subarray [2,3] has the largest product = 6.

传送门

题意:给定一个数组,求连续的乘积最大值。

思路:dp,设f(k)为乘积最大序列,g(k)为成绩最小序列,则有

  • f(k) = max( A[k] , f(k-1) * A[k], A[k], g(k-1) * A[k] )
  • g(k) = min(A[k],   g(k-1) * A[k], A[k], f(k-1) * A[k] )

之所以要维护一个最小的序列,是因为负数*负数变成正数,此时可能为最大。

 

 

 

 

174. Dungeon Game

The demons had captured the princess (P) and imprisoned her in the bottom-right corner of a dungeon. The dungeon consists of M x N rooms laid out in a 2D grid. Our valiant knight (K) was initially positioned in the top-left room and must fight his way through the dungeon to rescue the princess.

The knight has an initial health point represented by a positive integer. If at any point his health point drops to 0 or below, he dies immediately.

Some of the rooms are guarded by demons, so the knight loses health (negative integers) upon entering these rooms; other rooms are either empty (0’s) or contain magic orbs that increase the knight’s health (positive integers).

In order to reach the princess as quickly as possible, the knight decides to move only rightward or downward in each step.

 

Write a function to determine the knight’s minimum initial health so that he is able to rescue the princess.

For example, given the dungeon below, the initial health of the knight must be at least 7 if he follows the optimal path RIGHT-> RIGHT -> DOWN -> DOWN.

-2 (K) -3 3
-5 -10 1
10 30 -5 (P)

 

Notes:

  • The knight’s health has no upper bound.
  • Any room can contain threats or power-ups, even the first room the knight enters and the bottom-right room where the princess is imprisoned.

题目地址:leetcode Dungeon Game

题目大意:一个骑士从左上角出发,要到达右下角拯救公主,每个格子中有正数表示可以增加的生命值,负数表示减少的生命。如果生命值为0,那么骑士就死翘翘了。你的任务是帮骑士计算到达右下角的最小生命值。

思路:DP,设dp[i][j]为到达(i,j)需要的最少生命值。则有:

  • dp[i][j]= max ( 1 ,   min(dp[i+1][j],dp[i][j+1])-dungeon[i][j]   )

即,从右下角到左上角进行计算。对于dungeon为正数,那么减去,说明生命值可以少一些到达这个格子(但是不能小于等于0),对于负数,减去一个负数意味着加上这个数的绝对值,即需要的生命数增加。

为什么是右下角到左上角而不是左上到右下呢?

如果途中遇到一个很大的正数,它就会覆盖掉你之前走过的所有信息。(变为0),这样的结果是错误的。

C++

Python

 

 

 

198. House Robber

You are a professional robber planning to rob houses along a street. Each house has a certain amount of money stashed, the only constraint stopping you from robbing each of them is that adjacent houses have security system connected and it will automatically contact the police if two adjacent houses were broken into on the same night.

Given a list of non-negative integers representing the amount of money of each house, determine the maximum amount of money you can rob tonight without alerting the police.

传送门

题意:要求从一串数字中选取一些数,使得和最大(相邻不能两两)

思路:dp[i]为到i能获得的最大值

  • i选择,那么只能dp[i-2]+num[i]
  • i不选,那么dp[i-1]

所以有:dp[i]=max(dp[i-1],dp[i-2]+num[i])

 

213. House Robber II

Note: This is an extension of House Robber.

After robbing those houses on that street, the thief has found himself a new place for his thievery so that he will not get too much attention. This time, all houses at this place are arranged in a circle. That means the first house is the neighbor of the last one. Meanwhile, the security system for these houses remain the same as for those in the previous street.

Given a list of non-negative integers representing the amount of money of each house, determine the maximum amount of money you can rob tonight without alerting the police.

传送门

题意:和198. House Robber差别在于,这题要求首尾不能同时取到。

思路:和上一题其实一样的,我们只需要考虑[0,n-2]和[1,n-1]两个中最大值即可。。。

 

 

221. Maximal Square

Given a 2D binary matrix filled with 0’s and 1’s, find the largest square containing all 1’s and return its area.

For example, given the following matrix:

Return 4.

传送门

题意:给定2D矩阵,求里面1构成的正方形的最大面积。

思路:dp

  • if matrix[i][j]==’1′  dp[i][j] = min(dp[i-1][j],dp[i][j-1],dp[i-1][j-1])+1
  • else dp[i][j] = 0

Python

 

 


 

712. Minimum ASCII Delete Sum for Two Strings

Given two strings s1, s2, find the lowest ASCII sum of deleted characters to make two strings equal.

Example 1:

Example 2:

Note:

 

  • 0 < s1.length, s2.length <= 1000.
  • All elements of each string will have an ASCII value in [97, 122].

 

题目地址:leetcode Minimum ASCII Delete Sum for Two Strings

题目大意:给定两个字符串,要求删掉一些字母,使得它们相等。要求删除字母的ASCII码的和最小。

思路:

显然,删掉的字母越少越好,自然就想到了求最长公共子串。这样,答案就是 两个字符串的ASCII码的和 – 公共子串ASCII码的和 * 2

如果有多个子串的话,就取和最大那个即可。

于是,可以类似LCS的解法,设dp[i][j]为s1前i个字符和s2前j个字符的最大公共子串的ASCII码的最大的和

于是有:

  • s1[i] == s2[j] :    dp[i][j] = dp[i-1][j-1] + ascii(s1[i])
  • s1[i] != s2[j] :     dp[i][j] = max(dp[i-1][j] , dp[i][j – 1] )

C++

Java

Python

 

 


 

718. Maximum Length of Repeated Subarray

Given two integer arrays A and B, return the maximum length of an subarray that appears in both arrays.

Example 1:

Note:

  1. 1 <= len(A), len(B) <= 1000
  2. 0 <= A[i], B[i] < 100

题目地址:leetcode Maximum Length of Repeated Subarray

题目大意: 给定两个数组A和B,求他们最大的公共子数组。

思路:

类似于LCS,这题也是DP

设dp[i][j] 为以A[i],B[j] 结尾的最大公共子数组的长度。

于是有

  • if A[i – 1] == B[j – 1]   dp[i][j] = dp[i – 1][j – 1] + 1
  • A[i – 1] != B[j – 1]    dp[i][j] = 0

ans则为dp数组的最大值

Java

 

还可以用滚动数组优化空间复杂度O(n)

Java

Python

 

 


 

799. Champagne Tower

We stack glasses in a pyramid, where the first row has 1 glass, the second row has 2 glasses, and so on until the 100th row.  Each glass holds one cup (250ml) of champagne.

Then, some champagne is poured in the first glass at the top.  When the top most glass is full, any excess liquid poured will fall equally to the glass immediately to the left and right of it.  When those glasses become full, any excess champagne will fall equally to the left and right of those glasses, and so on.  (A glass at the bottom row has it’s excess champagne fall on the floor.)

For example, after one cup of champagne is poured, the top most glass is full.  After two cups of champagne are poured, the two glasses on the second row are half full.  After three cups of champagne are poured, those two cups become full – there are 3 full glasses total now.  After four cups of champagne are poured, the third row has the middle glass half full, and the two outside glasses are a quarter full, as pictured below.

Now after pouring some non-negative integer cups of champagne, return how full the j-th glass in the i-th row is (both i and j are 0 indexed.)

 

Example 1:
Input: poured = 1, query_glass = 1, query_row = 1
Output: 0.0
Explanation: We poured 1 cup of champange to the top glass of the tower (which is indexed as (0, 0)). There will be no excess liquid so all the glasses under the top glass will remain empty.

Example 2:
Input: poured = 2, query_glass = 1, query_row = 1
Output: 0.5
Explanation: We poured 2 cups of champange to the top glass of the tower (which is indexed as (0, 0)). There is one cup of excess liquid. The glass indexed as (1, 0) and the glass indexed as (1, 1) will share the excess liquid equally, and each will get half cup of champange.

 

Note:

poured will be in the range of [0, 10 ^ 9].
query_glass and query_row will be in the range of [0, 99].

题目地址:leetcode Champagne Tower

题目大意:第0排1个杯子,第1排2个杯子以此类推。当某个杯子装满水,溢出的水均匀的向下一排的两边扩散。给定n杯水,问第i行第j个杯子的满水情况。

思路:直接dp

若dp[i][j] > 1

  • dp[i + 1][j] += dp[i][j]
  • dp[i + 1][j + 1] += dp[i][j]

就是说当前的i,j可以流向下一行的第j个和第j+1个

C++

Python

另一种写法:

Python

 


 

818. Race Car

Your car starts at position 0 and speed +1 on an infinite number line.  (Your car can go into negative positions.)

Your car drives automatically according to a sequence of instructions A (accelerate) and R (reverse).

When you get an instruction “A”, your car does the following: position += speed, speed *= 2.

When you get an instruction “R”, your car does the following: if your speed is positive then speed = -1 , otherwise speed = 1.  (Your position stays the same.)

For example, after commands “AAR”, your car goes to positions 0->1->3->3, and your speed goes to 1->2->4->-1.

Now for some target position, say the length of the shortest sequence of instructions to get there.

Example 1:
Input:
target = 3
Output: 2
Explanation:
The shortest instruction sequence is “AA”.
Your position goes from 0->1->3.

Example 2:
Input:
target = 6
Output: 5
Explanation:
The shortest instruction sequence is “AAARA”.
Your position goes from 0->1->3->7->7->6.

Note:

  • 1 <= target <= 10000.

题目地址:leetcode Race Car

题目大意:你可以做两种操作’A’和‘R’, A即position += speed, speed *= 2. R即让你速度反向,并且大小为1.   问你用这两种操作到达target的最小步数。

思路:

DP,设dp[i]为到达i的最小步数。

则对于i = 2^k – 1,走k步即可(没有更优的了)

对于2^(k-1) < i < 2^k

  • 先走到2^(k-1) – 1处(A^(k-1)),然后掉头,然后走A^x, 然后掉头,走dp[i – (2^(k -1) – 1)+ 2^(x) – 1],所以总共为k-1 +  1 + x + 1 + dp[i – (2^(k -1) – 1)+ 2^(x) – 1]
  • 走到2^k – 1,然后掉头,走dp[2^k -1 – i]  总共 k + 1+ dp[2^k – 1- target]

C++

Python

 


更多题解可以查看: https://www.hrwhisper.me/leetcode-algorithm-solution/

本博客若无特殊说明则由 hrwhisper 原创发布
转载请点名出处:细语呢喃 > leetcode 动态规划(DP)
本文地址:https://www.hrwhisper.me/leetcode-dynamic-programming/

听说长得好看的已经打赏了

Leetcode . permalink.

2 thoughts on “leetcode 动态规划(DP)

Leave a Reply

Your email address will not be published. Required fields are marked *