57
57
<!-- <div align="center"><img src="https://latex.codecogs.com/gif.latex?dp[i]=dp[i-1]+dp[i-2]" class="mathjax-pic"/></div> <br>-->
58
58
59
59
<div align =" center " > <img src =" pics/14fe1e71-8518-458f-a220-116003061a83.png " width =" 200px " > </div ><br >
60
+
60
61
考虑到 dp[ i] 只与 dp[ i - 1] 和 dp[ i - 2] 有关,因此可以只用两个变量来存储 dp[ i - 1] 和 dp[ i - 2] ,使得原来的 O(N) 空间复杂度优化为 O(1) 复杂度。
61
62
62
63
``` java
@@ -87,6 +88,7 @@ public int climbStairs(int n) {
87
88
<!-- <div align="center"><img src="https://latex.codecogs.com/gif.latex?dp[i]=max(dp[i-2]+nums[i],dp[i-1])" class="mathjax-pic"/></div> <br>-->
88
89
89
90
<div align =" center " > <img src =" pics/2de794ca-aa7b-48f3-a556-a0e2708cb976.jpg " width =" 350px " > </div ><br >
91
+
90
92
``` java
91
93
public int rob(int [] nums) {
92
94
int pre2 = 0 , pre1 = 0 ;
@@ -140,6 +142,7 @@ private int rob(int[] nums, int first, int last) {
140
142
<!-- <div align="center"><img src="https://latex.codecogs.com/gif.latex?dp[i]=(i-1)*dp[i-2]+(i-1)*dp[i-1]" class="mathjax-pic"/></div> <br>-->
141
143
142
144
<div align =" center " > <img src =" pics/da1f96b9-fd4d-44ca-8925-fb14c5733388.png " width =" 350px " > </div ><br >
145
+
143
146
## 5. 母牛生产
144
147
145
148
[ 程序员代码面试指南-P181] ( # )
@@ -151,6 +154,7 @@ private int rob(int[] nums, int first, int last) {
151
154
<!-- <div align="center"><img src="https://latex.codecogs.com/gif.latex?dp[i]=dp[i-1]+dp[i-3]" class="mathjax-pic"/></div> <br>-->
152
155
153
156
<div align =" center " > <img src =" pics/879814ee-48b5-4bcb-86f5-dcc400cb81ad.png " width =" 250px " > </div ><br >
157
+
154
158
# 矩阵路径
155
159
156
160
## 1. 矩阵的最小路径和
@@ -196,6 +200,7 @@ public int minPathSum(int[][] grid) {
196
200
题目描述:统计从矩阵左上角到右下角的路径总数,每次只能向右或者向下移动。
197
201
198
202
<div align =" center " > <img src =" pics/dc82f0f3-c1d4-4ac8-90ac-d5b32a9bd75a.jpg " width =" " > </div ><br >
203
+
199
204
``` java
200
205
public int uniquePaths(int m, int n) {
201
206
int [] dp = new int [n];
@@ -416,6 +421,7 @@ public int numDecodings(String s) {
416
421
<!-- <div align="center"><img src="https://latex.codecogs.com/gif.latex?dp[n]=max\{1,dp[i]+1|S_i<S_n\&\&i<n\}" class="mathjax-pic"/></div> <br>-->
417
422
418
423
<div align =" center " > <img src =" pics/ee994da4-0fc7-443d-ac56-c08caf00a204.jpg " width =" 350px " > </div ><br >
424
+
419
425
对于一个长度为 N 的序列,最长递增子序列并不一定会以 S<sub >N</sub > 为结尾,因此 dp[ N] 不是序列的最长递增子序列的长度,需要遍历 dp 数组找出最大值才是所要的结果,max{ dp[ i] | 1 <= i <= N} 即为所求。
420
426
421
427
## 1. 最长递增子序列
@@ -582,6 +588,7 @@ public int wiggleMaxLength(int[] nums) {
582
588
<!-- <div align="center"><img src="https://latex.codecogs.com/gif.latex?dp[i][j]=\left\{\begin{array}{rcl}dp[i-1][j-1]&&{S1_i==S2_j}\\max(dp[i-1][j],dp[i][j-1])&&{S1_i<>S2_j}\end{array}\right." class="mathjax-pic"/></div> <br>-->
583
589
584
590
<div align =" center " > <img src =" pics/ecd89a22-c075-4716-8423-e0ba89230e9a.jpg " width =" 450px " > </div ><br >
591
+
585
592
对于长度为 N 的序列 S<sub >1</sub > 和长度为 M 的序列 S<sub >2</sub >,dp[ N] [ M ] 就是序列 S<sub >1</sub > 和序列 S<sub >2</sub > 的最长公共子序列长度。
586
593
587
594
与最长递增子序列相比,最长公共子序列有以下不同点:
@@ -621,6 +628,7 @@ public int lengthOfLCS(int[] nums1, int[] nums2) {
621
628
<!-- <div align="center"><img src="https://latex.codecogs.com/gif.latex?dp[i][j]=max(dp[i-1][j],dp[i-1][j-w]+v)" class="mathjax-pic"/></div> <br>-->
622
629
623
630
<div align =" center " > <img src =" pics/8cb2be66-3d47-41ba-b55b-319fc68940d4.png " width =" 400px " > </div ><br >
631
+
624
632
``` java
625
633
// W 为背包总体积
626
634
// N 为物品数量
@@ -649,6 +657,7 @@ public int knapsack(int W, int N, int[] weights, int[] values) {
649
657
<!-- <div align="center"><img src="https://latex.codecogs.com/gif.latex?dp[j]=max(dp[j],dp[j-w]+v)" class="mathjax-pic"/></div> <br>-->
650
658
651
659
<div align =" center " > <img src =" pics/9ae89f16-7905-4a6f-88a2-874b4cac91f4.jpg " width =" 300px " > </div ><br >
660
+
652
661
因为 dp[ j-w] 表示 dp[ i-1] [ j-w ] ,因此不能先求 dp[ i] [ j-w ] ,防止将 dp[ i-1] [ j-w ] 覆盖。也就是说要先计算 dp[ i] [ j ] 再计算 dp[ i] [ j-w ] ,在程序实现时需要按倒序来循环求解。
653
662
654
663
``` java
@@ -860,17 +869,20 @@ return -1.
860
869
861
870
``` java
862
871
public int coinChange(int [] coins, int amount) {
863
- int [] dp = new int [amount + 1 ];
864
- Arrays . fill(dp, amount + 1 );
865
- dp[0 ] = 0 ;
866
- for (int i = 1 ; i < dp. length; i++ ) {
867
- for (int j = 0 ; j < coins. length; j++ ) {
868
- if (coins[j] <= i) {
869
- dp[i] = Math . min(dp[i], dp[i - coins[j]] + 1 );
870
- }
872
+ int [] dp = new int [amount + 1 ];
873
+ for (int coin : coins) {
874
+ for (int i = coin; i <= amount; i++ ) { // 将逆序遍历改为正序遍历
875
+ if (i == coin) {
876
+ dp[i] = 1 ;
877
+ } else if (dp[i] == 0 && dp[i - coin] != 0 ) {
878
+ dp[i] = dp[i - coin] + 1 ;
879
+
880
+ } else if (dp[i - coin] != 0 ) {
881
+ dp[i] = Math . min(dp[i], dp[i - coin] + 1 );
871
882
}
872
883
}
873
- return dp[amount] > amount ? - 1 : dp[amount];
884
+ }
885
+ return dp[amount] == 0 ? - 1 : dp[amount];
874
886
}
875
887
```
876
888
@@ -892,7 +904,7 @@ Explanation: there are four ways to make up the amount:
892
904
893
905
``` java
894
906
public int change(int amount, int [] coins) {
895
- if (amount == 0 || coins == null || coins . length == 0 ) {
907
+ if (coins == null ) {
896
908
return 0 ;
897
909
}
898
910
int [] dp = new int [amount + 1 ];
@@ -992,8 +1004,7 @@ public int combinationSum4(int[] nums, int target) {
992
1004
993
1005
题目描述:交易之后需要有一天的冷却时间。
994
1006
995
- <div align =" center " > <img src =" pics/83acbb02-872a-4178-b22a-c89c3cb60263.jpg " width =" 300px " > </div ><br >
996
-
1007
+ <div align =" center " > <img src =" pics/ffd96b99-8009-487c-8e98-11c9d44ef14f.png " width =" 300px " > </div ><br >
997
1008
998
1009
``` java
999
1010
public int maxProfit(int [] prices) {
@@ -1035,6 +1046,7 @@ The total profit is ((8 - 1) - 2) + ((9 - 4) - 2) = 8.
1035
1046
题目描述:每交易一次,都要支付一定的费用。
1036
1047
1037
1048
<div align =" center " > <img src =" pics/1e2c588c-72b7-445e-aacb-d55dc8a88c29.png " width =" 300px " > </div ><br >
1049
+
1038
1050
``` java
1039
1051
public int maxProfit(int [] prices, int fee) {
1040
1052
int N = prices. length;
0 commit comments