-
Notifications
You must be signed in to change notification settings - Fork 160
/
Copy pathDynamic_Programming.md
1458 lines (1119 loc) · 44.2 KB
/
Dynamic_Programming.md
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<span id = "00"></span>
## 一维
- [70. Climbing Stairs](#70-climbing-stairs)
- [53. Maximum Subarray](#53-maximum-subarray)
- [62. Unique Paths](#62-unique-paths)
- [63. Unique Paths II](#63-unique-paths-ii)
- [120. Triangle 很少考](#120-triangle)
- [279. Perfect Squares](#279-perfect-squares)
- [139. Word Break](#139-word-break)
- [375. Guess Number Higher or Lower II](#375-guess-number-higher-or-lower-ii)
- [312. Burst Balloons](#312-burst-balloons)
- [322. Coin Change](#322-coin-change)
## 股票最大利润问题
- [121. Best Time to Buy and Sell Stock](#121-best-time-to-buy-and-sell-stock)
- [122. Best Time to Buy and Sell Stock II](#122-best-time-to-buy-and-sell-stock-ii)
- [123. Best Time to Buy and Sell Stock III](#123-best-time-to-buy-and-sell-stock-iii)
- [188 Best Time to Buy and Sell Stock IV]
- [309. Best Time to Buy and Sell Stock with Cooldown](#309-best-time-to-buy-and-sell-stock-with-cooldown)
## 二维
- [256 Paint House] **?**
- [265 Paint House II] **?**
- [64. Minimum Path Sum](#64-minimum-path-sum)
- [72. Edit Distance](#72-edit-distance)
- [300. Longest Increasing Subsequence](#300-longest-increasing-subsequence)
- [1143. Longest Common Subsequence](#1143-longest-common-subsequence)
- [97. Interleaving String](#97-interleaving-string)
- [174 Dungeon Game]
- [221. Maximal Square](#221-maximal-square)
- [85 Maximal Rectangle]
- [363 Max Sum of Rectangle No Larger Than K]
## 化简
- [198. House Robber](#198-house-robber)
- [213. House Robber II](#213-house-robber-ii)
- [276 Paint Fence]
- [91. Decode Ways](#91-decode-ways)
- [10. Regular Expression Matching](#10-regular-expression-matching)
- [44. Wildcard Matching](#44-wildcard-matching)
## 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.
你正在爬楼梯。它需要n步才能达到顶峰。
每次你可以爬1或2步。您可以通过多少不同的方式登顶?
注意:给定n将是一个正整数。
**Example**
```
Input: 2
Output: 2
Explanation: There are two ways to climb to the top.
1. 1 step + 1 step
2. 2 steps
```
---
### Python Solution
**分析:** 从简单到难理解动态规划,即利用前面的子问题的解求得后面问题的解。不递归调用,而只是存储子问题解并且通过状态转移来解决。
```python
class Solution:
def climbStairs(self, n: int) -> int:
if n <= 2:
return n
dp = [0] * n
dp[0], dp[1] = 1, 2
for i in range(2,n):
dp[i] = dp[i - 1] + dp[i- 2]
return dp[-1]
```
```python
class Solution:
def climbStairs(self, n: int) -> int:
a, b = 1, 0
for _ in range(n + 1):
a, b = a + b, a
return b
```
[返回目录](#00)
## 53. Maximum Subarray
Given an integer array nums, find the contiguous subarray (containing at least one number) which has the largest sum and return its sum.
给定一个整数数组nums,找到具有最大总和的连续子数组(至少包含一个数字)并返回其总和。
**Example**
```
Input: [-2,1,-3,4,-1,2,1,-5,4],
Output: 6
Explanation: [4,-1,2,1] has the largest sum = 6.
```
---
### Python Solution
**分析:** 动态规划做法及其优化。实际上用 best 存储当前 dp 的最大值, curr 存储 dp[i]。
```python
class Solution:
def maxSubArray(self, nums: List[int]) -> int:
dp = [0 for _ in range(len(nums))]
dp[0] = nums[0]
for i in range(1, len(nums)):
dp[i] = max(dp[i-1]+nums[i], nums[i])
return max(dp)
```
```python
class Solution:
def maxSubArray(self, nums: List[int]) -> int:
best = curr = nums[0]
for i in range(1, len(nums)):
curr = max(nums[i], curr + nums[i])
best = max(best, curr)
return best
```
[返回目录](#00)
## 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?
机器人位于 m x n 矩阵的左上角(在下图中标记为“开始”)。 机器人只能在任何时间点上下移动。 机器人试图到达网格的右下角(在下图中标记为“完成”)。 有多少种可能的独特路径?
**Example**
```
Example 1:
Input: m = 3, n = 2
Output: 3
Explanation:
From the top-left corner, there are a total of 3 ways to reach the bottom-right corner:
1. Right -> Right -> Down
2. Right -> Down -> Right
3. Down -> Right -> Right
Example 2:
Input: m = 7, n = 3
Output: 28
```
---
### Python Solution
**分析:**
```python
class Solution:
def uniquePaths(self, m: int, n: int) -> int:
dp = [[1 for _ in range(n)] for _ in range(m)]
for i in range(1, m):
for j in range(1, n):
dp[i][j] = dp[i-1][j] + dp[i][j-1]
return dp[-1][-1]
```
**可简化为一维**
```python
class Solution:
def uniquePaths(self, m: int, n: int) -> int:
dp = [1 for _ in range(n)]
for i in range(1, m):
for j in range(1, n):
dp[j] = dp[j] + dp[j-1]
return dp[-1]
```
**数学方法**
```python
from math import factorial
class Solution:
def uniquePaths(self, m: int, n: int) -> int:
return int(factorial(n+m-2) / (factorial(n-1) * factorial(m-1)))
```
[返回目录](#00)
## 63. Unique Paths II
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).
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.
机器人位于m x n网格的左上角(在下图中标记为“开始”)。 机器人只能在任何时间点上下移动。 机器人试图到达网格的右下角(在下图中标记为“完成”)。 现在考虑是否在网格中添加了一些障碍。 会有多少条独特的路径? 障碍物和空白区域在网格中分别标记为1和0。
**Example**
```
Input:
[
[0,0,0],
[0,1,0],
[0,0,0]
]
Output: 2
Explanation:
There is one obstacle in the middle of the 3x3 grid above.
There are two ways to reach the bottom-right corner:
1. Right -> Right -> Down -> Down
2. Down -> Down -> Right -> Right
```
---
### Python Solution
**分析:**
```python
class Solution:
def uniquePathsWithObstacles(self, obstacleGrid: List[List[int]]) -> int:
if not obstacleGrid or not obstacleGrid[0]:
return 0
if obstacleGrid[0][0] == 1 or obstacleGrid[-1][-1] == 1:
return 0
m, n = len(obstacleGrid), len(obstacleGrid[0])
dp = [[0 for x in range(n)] for y in range(m)]
#dp = [[0] * n] * m 和上一行的区别为这一行实际上是浅拷贝。非常不安全。
i = j = 0
while i < m and obstacleGrid[i][0] == 0:
dp[i][0] = 1
i += 1
while j < n and obstacleGrid[0][j] == 0:
dp[0][j] = 1
j += 1
for i in range(1, m):
for j in range(1, n):
if obstacleGrid[i][j] == 1:
dp[i][j] = 0
else:
dp[i][j] = dp[i - 1][j] + dp[i][j - 1]
return dp[-1][-1]
```
**可简化为一维**
```python
class Solution:
def uniquePathsWithObstacles(self, obstacleGrid: List[List[int]]) -> int:
if not obstacleGrid or not obstacleGrid[0]:
return 0
if obstacleGrid[0][0] == 1 or obstacleGrid[-1][-1] == 1:
return 0
m, n = len(obstacleGrid), len(obstacleGrid[0])
dp = [0 for _ in range(n)]
dp[0] = 1
for i in range(m):
if obstacleGrid[i][0] == 1:
dp[0] = 0
for j in range(1, n):
if obstacleGrid[i][j] == 0:
dp[j] = dp[j] + dp[j - 1]
else:
dp[j] = 0
return dp[-1]
```
[返回目录](#00)
## 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.
给定一个三角形,找到从上到下的最小路径总和。您可以将每一步移至下面一行中的相邻数字。
**Example**
```
For example, given the following triangle
[
[2],
[3,4],
[6,5,7],
[4,1,8,3]
]
The minimum path sum from top to bottom is 11 (i.e., 2 + 3 + 5 + 1 = 11).
```
---
### Python Solution
**分析:** 自底向上找到更小的值合并到最开始的一个元素。也可以自顶而下,不过需要预处理数据
```python
class Solution(object):
def minimumTotal(self, triangle):
dp = triangle[-1][:]
for i in range(len(triangle)-2, -1, -1):
for j in range(i+1):
dp[j] = min(dp[j], dp[j+1]) + triangle[i][j]
return dp[0]
```
**写成一行的形式:**
```python
class Solution(object):
def minimumTotal(self, triangle):
return reduce(lambda a, b:[min(a[i], a[i+1])+n for i, n in enumerate(b)], triangle[::-1])[0]
```
[返回目录](#00)
## 279. Perfect Squares
Given a positive integer n, find the least number of perfect square numbers (for example, 1, 4, 9, 16, ...) which sum to n.
给定一个正整数n,求和为n的最小平方数(例如1、4、9、16,...)。
**Example**
```
Example 1:
Input: n = 12
Output: 3
Explanation: 12 = 4 + 4 + 4.
Example 2:
Input: n = 13
Output: 2
Explanation: 13 = 4 + 9.
```
---
### Python Solution
**分析:** DP 解法。
```python
class Solution(object):
_dp = [0]
def numSquares(self, n):
dp = self._dp
while len(dp) <= n: # 下面这句话的意思是求出所以能加一个平方数(即一个操作)到目前位置的dp[?]。
dp += min(dp[-i*i] for i in range(1, int(len(dp)**0.5+1))) + 1,
return dp[n]
```
[返回目录](#00)
## 139. Word Break
Given a non-empty string s and a dictionary wordDict containing a list of non-empty words, determine if s can be segmented into a space-separated sequence of one or more dictionary words.
Note:
The same word in the dictionary may be reused multiple times in the segmentation.
You may assume the dictionary does not contain duplicate words.
给定一个非空字符串s和包含非空单词列表的字典wordDict,请确定s是否可以分段为一个或多个字典单词的以空格分隔的序列。
注意:
字典中的同一单词在细分中可能会重复使用多次。 您可能会认为词典中没有重复的单词。
**Example**
```
Example 1:
Input: s = "leetcode", wordDict = ["leet", "code"]
Output: true
Explanation: Return true because "leetcode" can be segmented as "leet code".
Example 2:
Input: s = "applepenapple", wordDict = ["apple", "pen"]
Output: true
Explanation: Return true because "applepenapple" can be segmented as "apple pen apple".
Note that you are allowed to reuse a dictionary word.
Example 3:
Input: s = "catsandog", wordDict = ["cats", "dog", "sand", "and", "cat"]
Output: false
```
---
### Python Solution
**分析:** 两种解法,一种是逐个遍历,True表示可以到达这个地方,一直推导到最后一个,之所以是 len(s)+1 是因为最后一位必须要取到。另一种是递归的做法。
```python
class Solution:
def wordBreak(self, s, words):
ok = [True]
max_len = max(map(len,words+['']))
words = set(words)
for i in range(1, len(s)+1):
ok += any(ok[j] and s[j:i] in words for j in range(max(0, i-max_len),i)),
return ok[-1]
```
```python
class Solution: # 效率很高!
def wordBreak(self, s: str, wordDict: List[str]) -> bool:
dic = dict()
def helper(s):
if not s: return True
elif s in dic:
return dic[s]
else:
dic[s]=any(helper(s[len(w):]) for w in wordDict if s.startswith(w))
return dic[s]
return helper(s)
```
[返回目录](#00)
## 375. Guess Number Higher or Lower II
We are playing the Guess Game. The game is as follows:
I pick a number from 1 to n. You have to guess which number I picked.
Every time you guess wrong, I'll tell you whether the number I picked is higher or lower.
However, when you guess a particular number x, and you guess wrong, you pay $x. You win the game when you guess the number I picked.
我们正在玩猜猜游戏。 游戏如下:我选择一个从1到n的数字。 您必须猜测我选了哪个号码。 每次您猜错了,我都会告诉您我选的数字是高还是低。 但是,当您猜一个特定的数字x时,如果您猜错了,您需要支付$ x。 当您猜到我选的号码时,您就赢得了比赛。
**Example**
```
n = 10, I pick 8.
First round: You guess 5, I tell you that it's higher. You pay $5.
Second round: You guess 7, I tell you that it's higher. You pay $7.
Third round: You guess 9, I tell you that it's lower. You pay $9.
Game over. 8 is the number I picked.
You end up paying $5 + $7 + $9 = $21.
```
---
### Python Solution
**分析:** 一种是自下向上的带备忘录的动态规划,一种是递归的做法。
```python
class Solution(object):
def getMoneyAmount(self, n):
need = [[0] * (n+1) for _ in range(n+1)]
for lo in range(n, 0, -1):
for hi in range(lo+1, n+1):
need[lo][hi] = min(x + max(need[lo][x-1], need[x+1][hi])
for x in range(lo, hi))
return need[1][n]
```
```python
class Solution(object):
def getMoneyAmount(self, n):
def play(l = 1, h = n, dp = {}):
if l >= h: return 0
if (l, h) not in dp:
dp[(l, h)] = min(g + max(play(l, g - 1, dp), play(g + 1, h, dp)) for g in range((l + h) / 2, h))
return dp[(l, h)]
return play()
```
[返回目录](#00)
## 312. Burst Balloons
Given n balloons, indexed from 0 to n-1. Each balloon is painted with a number on it represented by array nums. You are asked to burst all the balloons. If the you burst balloon i you will get nums[left] * nums[i] * nums[right] coins. Here left and right are adjacent indices of i. After the burst, the left and right then becomes adjacent.
Find the maximum coins you can collect by bursting the balloons wisely.
给定n个气球,索引从0到n-1。 每个气球上都涂有一个数字,该数字由数组num表示。 要求您爆破所有气球。 如果您爆破气球i,您将获得nums [left] * nums [i] * nums [right]个硬币。 这里的左和右是i的相邻索引。 爆破后,左右会相邻。 明智地爆破气球,找到可以收集的最大硬币数。
**Example**
```
Input: [3,1,5,8]
Output: 167
Explanation: nums = [3,1,5,8] --> [3,5,8] --> [3,8] --> [8] --> []
coins = 3*1*5 + 3*5*8 + 1*3*8 + 1*8*1 = 167
```
---
### Python Solution
**分析:** 这道题没搞懂,感觉和暴力一样,头大。
```python
class Solution:
def maxCoins(self, nums: List[int]) -> int:
nums = [1] + [x for x in nums if x > 0] + [1]
n = len(nums)
dp = [[0] * n for _ in range(n)]
for j in range(1, n):
for i in range(j - 2, -1, -1):
ans = 0
for mid in range(i + 1, j):
tmp = nums[i] * nums[mid] * nums[j] + dp[i][mid] + dp[mid][j]
if tmp > ans: ans = tmp
dp[i][j] = ans
return dp[0][n - 1]
```
[返回目录](#00)
## 322. Coin Change
You are given coins of different denominations and a total amount of money amount. Write a function to compute the fewest number of coins that you need to make up that amount. If that amount of money cannot be made up by any combination of the coins, return -1.
系统会为您提供不同面额的硬币和总金额。 编写一个函数来计算组成该数量所需的最少数量的硬币。 如果这笔钱不能用硬币的任何组合来弥补,请返回-1。
**Example**
```
Example 1:
Input: coins = [1, 2, 5], amount = 11
Output: 3
Explanation: 11 = 5 + 5 + 1
Example 2:
Input: coins = [2], amount = 3
Output: -1
```
---
### Python Solution
**分析:** 经典的换零钱问题,需要着重掌握。
```python
class Solution:
def coinChange(self, coins, amt):
def do(count, have, i, res):
coin = coins[i]
if count - (have - amt) // coin >= res:
return res
need = amt-have
if need % coin is 0:
x = count + need//coin
return x if x < res else res
if i is 0:
return res
for j in range(need // coin, -1, -1):
sub = do(count+j, have+coin*j, i-1, res)
if sub < res:
res = sub
return res
coins.sort()
ret = do(0, 0, len(coins)-1, amt+1)
return -1 if ret == amt+1 else ret
```
```python
class Solution:
def coinChange(self, coins, amount):
MAX = float('inf')
dp = [0] + [MAX] * amount
for i in range(1, amount + 1):
dp[i] = min([dp[i - c] if i - c >= 0 else MAX for c in coins]) + 1
return [dp[amount], -1][dp[amount] == MAX]
```
[返回目录](#00)
## 121. Best Time to Buy and Sell Stock
Say you have an array for which the ith element is the price of a given stock on day i.
If you were only permitted to complete at most one transaction (i.e., buy one and sell one share of the stock), design an algorithm to find the maximum profit.
假设您有一个数组,第i个元素是第i天给定股票的价格。
如果只允许您最多完成一笔交易(即买入和卖出一股股票),请设计一种算法以找到最大的利润。
**Example**
```
Input: [7,1,5,3,6,4]
Output: 5
Explanation: Buy on day 2 (price = 1) and sell on day 5 (price = 6), profit = 6-1 = 5.
Not 7-1 = 6, as selling price needs to be larger than buying price.
```
---
### Python Solution
**分析:**
```python
class Solution:
def maxProfit(self, prices: List[int]) -> int:
profit = 0
low = 1 << 33
for price in prices:
if price < low:
low = price
elif price - low > profit:
profit = price - low
return profit
```
```python
class Solution:
def maxProfit(self, prices):
min_p, max_v = 1 << 33, 0
for i in range(len(prices)):
min_p = min(min_p, prices[i])
max_v = max(max_v, prices[i] - min_p)
return max_v
```
[返回目录](#00)
## 122. Best Time to Buy and Sell Stock II
Say you have an array for which the ith element is the price of a given stock on day i.
Design an algorithm to find the maximum profit. You may complete as many transactions as you like (i.e., buy one and sell one share of the stock multiple times).
Note: You may not engage in multiple transactions at the same time (i.e., you must sell the stock before you buy again).
假设您有一个数组,第i个元素是第i天给定股票的价格。 设计算法以找到最大的利润。 您可以根据需要完成尽可能多的交易(即,买入一份并多次出售一股股票)。 注意:您可能无法同时进行多项交易(即必须先出售股票才能再次购买)。
**Example**
```
Example 1:
Input: [7,1,5,3,6,4]
Output: 7
Explanation: Buy on day 2 (price = 1) and sell on day 3 (price = 5), profit = 5-1 = 4.
Then buy on day 4 (price = 3) and sell on day 5 (price = 6), profit = 6-3 = 3.
Example 2:
Input: [1,2,3,4,5]
Output: 4
Explanation: Buy on day 1 (price = 1) and sell on day 5 (price = 5), profit = 5-1 = 4.
Note that you cannot buy on day 1, buy on day 2 and sell them later, as you are
engaging multiple transactions at the same time. You must sell before buying again.
```
---
### Python Solution
**分析:** 两种解法:1. 动态规划的解法。 2. 贪心的解法,因为不限制买卖次数,所以我们只要后一天比前一天价格高,我们就在答案里加上差值,可以看作我们前一天买了今天买了,如果明天的比今天还高也没有关系,今天再买入、明天卖掉的方案和昨天买明天卖的方案是等价的。
```python
class Solution:
def maxProfit(self, prices: List[int]) -> int:
n = len(prices)
dp, j = [0] * n, n-1
for i in range(n-2, -1, -1):
if prices[i] > prices[i+1]:
j = i
dp[i] = dp[i+1] if i == j else prices[j] - prices[i] + dp[j]
return dp[0] if dp else 0
```
```python
class Solution:
def maxProfit(self, prices: List[int]) -> int:
res = 0
for i in range(1, len(prices)):
if prices[i] > prices[i-1]:
res += prices[i] - prices[i-1]
return res
# 可以简化为:
class Solution:
def maxProfit(self, prices: List[int]) -> int:
return sum(max(b-a,0)for a,b in zip(prices,prices[1:]))
```
[返回目录](#00)
## 123. Best Time to Buy and Sell Stock III
Say you have an array for which the ith element is the price of a given stock on day i.
Design an algorithm to find the maximum profit. You may complete at most two transactions.
Note: You may not engage in multiple transactions at the same time (i.e., you must sell the stock before you buy again).
假设您有一个数组,第i个元素是第i天给定股票的价格。 设计算法以找到最大的利润。 您最多可以完成两次交易。 注意:您可能无法同时进行多项交易(即必须先出售股票才能再次购买)。
**Example**
```
Example 1:
Input: [3,3,5,0,0,3,1,4]
Output: 6
Explanation: Buy on day 4 (price = 0) and sell on day 6 (price = 3), profit = 3-0 = 3.
Then buy on day 7 (price = 1) and sell on day 8 (price = 4), profit = 4-1 = 3.
Example 2:
Input: [1,2,3,4,5]
Output: 4
Explanation: Buy on day 1 (price = 1) and sell on day 5 (price = 5), profit = 5-1 = 4.
Note that you cannot buy on day 1, buy on day 2 and sell them later, as you are
engaging multiple transactions at the same time. You must sell before buying again.
```
---
### Python Solution
**分析:**
**一维的解法**
```python
class Solution:
def maxProfit(self, prices: List[int]) -> int:
hold1 = hold2 = float('-inf')
sold1 = sold2 = 0
for v in prices:
sold2 = max(sold2, hold2 + v)
hold2 = max(hold2, sold1 - v)
sold1 = max(sold1, hold1 + v)
hold1 = max(hold1, -v)
return sold2
```
[返回目录](#00)
## 309. Best Time to Buy and Sell Stock with Cooldown
Say you have an array for which the ith element is the price of a given stock on day i.
Design an algorithm to find the maximum profit. You may complete as many transactions as you like (ie, buy one and sell one share of the stock multiple times) with the following restrictions:
- You may not engage in multiple transactions at the same time (ie, you must sell the stock before you buy again).
- After you sell your stock, you cannot buy stock on next day. (ie, cooldown 1 day)
假设您有一个数组,第i个元素是第i天给定股票的价格。
设计算法以找到最大的利润。 您可以根据需要完成任意数量的交易(即多次购买一股股票并卖出一股股票),但有以下限制:
- 您不能同时进行多笔交易(即必须先卖出股票才能进行交易) 再次购买)。
- 出售股票后,您将无法在第二天购买股票。 (即冷却1天)
**Example**
```
Example 1:
Input: [1,2,3,0,2]
Output: 3
Explanation: transactions = [buy, sell, cooldown, buy, sell]
```
---
### Python Solution
**分析:** 两种解法:1. 动态规划的解法。dp里每个元素第一位为没有股票的时候,第二位为持有股票的时候。 2. 简化的动态规划解法,其实转移的状态只有3种状态,所以可以优化
```python
class Solution:
def maxProfit(self, prices: List[int]) -> int:
n = len(prices)
if n < 2: return 0
dp = [[0] * 2 for _ in range(n)]
dp[0][1] = -prices[0];
dp[1][0] = max(0, prices[1] - prices[0])
dp[1][1] = -min(prices[0], prices[1])
for i in range(2, n):
dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] + prices[i])
dp[i][1] = max(dp[i - 1][1], dp[i - 2][0] - prices[i])
return dp[-1][0]
```
```python
class Solution:
def maxProfit(self, prices: List[int]) -> int:
sold, rest, hold = 0, 0, float('-inf')
for p in prices:
sold, hold, rest = max(sold, hold + p), max(hold, rest - p), max(rest, sold)
return max(sold, rest)
```
[返回目录](#00)
## 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.
给定一个m×n的网格,其中填充有非负数,请找到一条从左上到右下的路径,该路径将沿其路径的所有数字的总和最小化。
注意:您只能在任何时间点向下或向右移动。
**Example**
```
Input:
[
[1,3,1],
[1,5,1],
[4,2,1]
]
Output: 7
Explanation: Because the path 1→3→1→1→1 minimizes the sum.
```
---
### Python Solution
**分析:** 二维的动态规划,是比较简单的题目,有点类似于[剑指 offer #47 礼物最大价值](https://github.com/YaxeZhang/Just-Interview/blob/master/%E5%89%91%E6%8C%87offer%EF%BC%88%E7%AC%AC%E4%BA%8C%E7%89%88%EF%BC%89/%E5%89%91%E6%8C%87offer%EF%BC%88%E7%AC%AC%E4%BA%8C%E7%89%88%EF%BC%89.md#47%E7%A4%BC%E7%89%A9%E7%9A%84%E6%9C%80%E5%A4%A7%E4%BB%B7%E5%80%BC),但是区别在于最大价值对于第一行的初始化不用特意初始化,而求最小的值的话第一行是必须向后逐个累加的,所以第一行初始化要用前缀和。当然二维的可以写成一维的,这里就只给出一维 dp 的写法。
**一维的解法**
```python
class Solution:
def minPathSum(self, grid: List[List[int]]) -> int:
m, n = len(grid), len(grid[0])
dp = grid[0][:]
for i in range(1, n):
dp[i] += dp[i-1]
for i in range(1, m):
for j in range(n):
if j > 0:
dp[j] = grid[i][j] + min(dp[j], dp[j-1])
else:
dp[j] = grid[i][j] + dp[j]
return dp[-1]
```
[返回目录](#00)
## 72. Edit Distance
Given two words word1 and word2, find the minimum number of operations required to convert word1 to word2.
You have the following 3 operations permitted on a word:
1. Insert a character
2. Delete a character
3. Replace a character
给定两个单词word1和word2,找到将word1转换为word2所需的最少操作数。
您可以对一个单词进行以下3个操作:
1. 插入一个字符
2. 删除一个字符
3. 替换一个字符
**Example**
```
Example 1:
Input: word1 = "horse", word2 = "ros"
Output: 3
Explanation:
horse -> rorse (replace 'h' with 'r')
rorse -> rose (remove 'r')
rose -> ros (remove 'e')
Example 2:
Input: word1 = "intention", word2 = "execution"
Output: 5
Explanation:
intention -> inention (remove 't')
inention -> enention (replace 'i' with 'e')
enention -> exention (replace 'n' with 'x')
exention -> exection (replace 'n' with 'c')
exection -> execution (insert 'u')
```
---
### Python Solution
**分析:** 十分经典的动态规划题目,如何理解要依靠表格来解决。可以发现 二维dp 转移是从左上角向右下角迭代的,之后不再是需求更之前的状态,所以可以简化为 一维dp 来表示。
```python
class Solution:
def minDistance(self, word1: str, word2: str) -> int:
m, n = len(word1), len(word2)
dp = [[0] * (n+1) for _ in range(m+1)]
for i in range(n+1):
dp[0][i] = i
for i in range(m+1):
dp[i][0] = i
for i in range(1, m+1):
for j in range(1, n+1):
if word1[i-1] == word2[j-1]:
dp[i][j] = dp[i-1][j-1]
else:
dp[i][j] = min(dp[i-1][j], dp[i-1][j-1], dp[i][j-1]) + 1
return dp[-1][-1]
```
**一维的解法**
```python
class Solution:
def minDistance(self, word1: str, word2: str) -> int:
m, n = len(word1), len(word2)
dp = list(range(n + 1))
for i in range(m):
new = [i + 1]
for j in range(n):
if word1[i] == word2[j]:
new.append(dp[j])
else:
new.append(1 + min(dp[j+1], dp[j],new[-1]))
dp = new
return dp[-1]
```
[返回目录](#00)
## 300. Longest Increasing Subsequence
Given an unsorted array of integers, find the length of longest increasing subsequence.
给定一个未排序的整数数组,请找到最长递增子序列的长度。
**Example**
```
Input: [10,9,2,5,3,7,101,18]
Output: 4
Explanation: The longest increasing subsequence is [2,3,7,101], therefore the length is 4.
```
---
### Python Solution
**分析:** 非常精彩的题目,常规动态规划解法是 O(n^2) 的时间复杂度,但是用二分搜索可以减少到 O(nlgn),尤其是迭代更新的部分需要好好品味下。
```python
class Solution:
def lengthOfLIS(self, nums: List[int]) -> int:
dp = [1] * len(nums)
for i in range(1, len(nums)):
for j in range(1, i+1):
if nums[i] > nums[i-j]:
dp[i] = max(dp[i], dp[i-j]+1)
return max(dp) if dp else 0
```
**O(nlgn)的解法**
```python
class Solution:
def lengthOfLIS(self, nums: List[int]) -> int:
if len(nums) < 2:
return len(nums)
dp = [nums[0]]
for n in nums[1:]:
if n > dp[-1]:
dp.append(n)
else:
left,right = 0, len(dp)
while left < right:
mid = left + (right-left)//2
if dp[mid] < n:
left = mid + 1
else:
right = mid
dp[left] = n
return len(dp)
```
[返回目录](#00)
## 1143. Longest Common Subsequence