Skip to content

Commit cb8c877

Browse files
author
robot
committed
feat: LIS
1 parent c2e69ba commit cb8c877

File tree

1 file changed

+106
-24
lines changed

1 file changed

+106
-24
lines changed

selected/LIS.md

Lines changed: 106 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -158,19 +158,15 @@ class Solution:
158158
if n == 0: return 0
159159
dp = [1] * n
160160
ans = 1
161-
intervals.sort(key=lambda a: a[1])
161+
intervals.sort(key=lambda a: a[0])
162162

163163
for i in range(len(intervals)):
164164
for j in range(i - 1, -1, -1):
165165
if intervals[i][0] >= intervals[j][1]:
166166
dp[i] = max(dp[i], dp[j] + 1)
167-
# 由于我事先进行了排序,因此倒着找的时候,找到的第一个一定是最大的数,因此不用往前继续找了。
168-
# 这也是为什么我按照结束时间排序的原因。
169-
break
170-
dp[i] = max(dp[i], dp[i - 1])
171-
ans = max(ans, dp[i])
167+
break # 由于是按照开始时间排序的, 因此可以剪枝
172168

173-
return n - ans
169+
return n - max(dp)
174170
```
175171

176172
**复杂度分析**
@@ -216,17 +212,20 @@ https://leetcode-cn.com/problems/maximum-length-of-pair-chain/
216212

217213
```py
218214
class Solution:
219-
def findLongestChain(self, pairs: List[List[int]]) -> int:
220-
n = len(pairs)
215+
def findLongestChain(self, intervals: List[List[int]]) -> int:
216+
n = len(intervals)
217+
if n == 0: return 0
221218
dp = [1] * n
222219
ans = 1
223-
pairs.sort(key=lambda a: a[0])
224-
for i in range(n):
225-
for j in range(i):
226-
if pairs[i][0] > pairs[j][1]:
220+
intervals.sort(key=lambda a: a[0])
221+
222+
for i in range(len(intervals)):
223+
for j in range(i - 1, -1, -1):
224+
if intervals[i][0] > intervals[j][1]:
227225
dp[i] = max(dp[i], dp[j] + 1)
228-
ans = max(ans, dp[i])
229-
return ans
226+
break # 由于是按照开始时间排序的, 因此可以剪枝
227+
228+
return max(dp)
230229
```
231230

232231
**复杂度分析**
@@ -272,19 +271,20 @@ Example:
272271

273272
```py
274273
class Solution:
275-
def findMinArrowShots(self, points: List[List[int]]) -> int:
276-
n = len(points)
274+
def findMinArrowShots(self, intervals: List[List[int]]) -> int:
275+
n = len(intervals)
277276
if n == 0: return 0
278277
dp = [1] * n
279-
cnt = 1
280-
points.sort(key=lambda a:a[1])
278+
ans = 1
279+
intervals.sort(key=lambda a: a[0])
281280

282-
for i in range(n):
283-
for j in range(0, i):
284-
if points[i][0] > points[j][1]:
281+
for i in range(len(intervals)):
282+
for j in range(i - 1, -1, -1):
283+
if intervals[i][0] > intervals[j][1]:
285284
dp[i] = max(dp[i], dp[j] + 1)
286-
cnt = max(cnt, dp[i])
287-
return cnt
285+
break # 由于是按照开始时间排序的, 因此可以剪枝
286+
287+
return max(dp)
288288
```
289289

290290
**复杂度分析**
@@ -313,6 +313,42 @@ class Solution:
313313
return len(d)
314314
```
315315

316+
如果求最长不递减子序列呢?
317+
318+
我们只需要将最左插入改为最右插入即可。代码:
319+
320+
```py
321+
class Solution:
322+
def lengthOfLIS(self, A: List[int]) -> int:
323+
d = []
324+
for a in A:
325+
# 这里改为最右
326+
i = bisect.bisect(d, a)
327+
if i < len(d):
328+
d[i] = a
329+
# 这里改为小于等号
330+
elif not d or d[-1] <= a:
331+
d.append(a)
332+
return len(d)
333+
```
334+
335+
最左插入和最右插入分不清的可以看看我的二分专题。
336+
337+
也可以这么写,更简单一点:
338+
339+
```py
340+
def LIS(A):
341+
d = []
342+
for a in A:
343+
# 如果求要严格递增就改为最左插入 bisect_left 即可
344+
i = bisect.bisect(d, a)
345+
if i == len(d):
346+
d.append(a)
347+
elif d[i] != a:
348+
d[i] = a
349+
return len(d)
350+
```
351+
316352
## More
317353

318354
其他的我就不一一说了。
@@ -408,6 +444,52 @@ class Solution:
408444
return len(target) - LIS(B)
409445
```
410446

447+
- [1626. 无矛盾的最佳球队](https://leetcode-cn.com/problems/best-team-with-no-conflicts/)
448+
449+
不就是先排下序,然后求 scores 的最长上升子序列么?
450+
451+
参考代码:
452+
453+
```py
454+
class Solution:
455+
def bestTeamScore(self, scores: List[int], ages: List[int]) -> int:
456+
n = len(scores)
457+
persons = list(zip(ages, scores))
458+
persons.sort(key=lambda x : (x[0], x[1]))
459+
dp = [persons[i][1] for i in range(n)]
460+
for i in range(n):
461+
for j in range(i):
462+
if persons[i][1] >= persons[j][1]:
463+
dp[i] = max(dp[i], dp[j]+persons[i][1])
464+
return max(dp)
465+
```
466+
467+
再比如 [这道题](https://binarysearch.com/problems/Circular-Longest-Increasing-Subsequence) 无非就是加了一个条件,我们可以结合循环移位的技巧来做。
468+
469+
> 关于循环移位算法西法在之前的文章 [文科生都能看懂的循环移位算法](https://lucifer.ren/blog/2020/02/20/rotate-list/) 也做了详细讲解,不再赘述。
470+
471+
参考代码:
472+
473+
```py
474+
class Solution:
475+
def solve(self, nums):
476+
n = len(nums)
477+
ans = 1
478+
def LIS(A):
479+
d = []
480+
for a in A:
481+
i = bisect.bisect_left(d,a)
482+
if i == len(d): d.append(a)
483+
else: d[i] = a
484+
return len(d)
485+
nums += nums
486+
for i in range(n):
487+
ans = max(ans , LIS(nums[i:i+n]))
488+
return ans
489+
```
490+
491+
大家把我讲的思路搞懂,这几个题一写,还怕碰到类似的题不会么?**只有熟练掌握基础的数据结构与算法,才能对复杂问题迎刃有余。** 最长上升子序列就是一个非常经典的基础算法,把它彻底搞懂,再去面对出题人的各种换皮就不怕了。相反,如果你不去思考题目背后的逻辑,就会刷地很痛苦。题目稍微一变化你就不会了,这也是为什么很多人说**刷了很多题,但是碰到新的题目还是不会做**的原因之一。关注公众号力扣加加,努力用清晰直白的语言还原解题思路,并且有大量图解,手把手教你识别套路,高效刷题。
492+
411493
更多题解可以访问我的 LeetCode 题解仓库:https://github.com/azl397985856/leetcode 。 目前已经 38K star 啦。
412494

413495
![](https://tva1.sinaimg.cn/large/007S8ZIlly1gfcuzagjalj30p00dwabs.jpg)

0 commit comments

Comments
 (0)