Skip to content

Commit 2cddbca

Browse files
author
lucifer
committed
feat: $801
1 parent 9d18ecf commit 2cddbca

File tree

3 files changed

+177
-0
lines changed

3 files changed

+177
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,7 @@ leetcode 题解,记录自己的 leetcode 解题之路。
324324
- [0754. 到达终点数字](./problems/754.reach-a-number.md)
325325
- [0785. 判断二分图](./problems/785.is-graph-bipartite.md)
326326
- [0799. 香槟塔](./problems/799.champagne-tower.md) 🆕
327+
- [0801. 使序列递增的最小交换次数](./problems/801.minimum-swaps-to-make-sequences-increasing.md) 🆕
327328
- [0816. 模糊坐标](./problems/816.ambiguous-coordinates.md)
328329
- [0820. 单词的压缩编码](./problems/820.short-encoding-of-words.md)
329330
- [0875. 爱吃香蕉的珂珂](./problems/875.koko-eating-bananas.md)

SUMMARY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,7 @@
188188
- [0754. 到达终点数字](./problems/754.reach-a-number.md)
189189
- [0785. 判断二分图](./problems/785.is-graph-bipartite.md)
190190
- [0799. 香槟塔](./problems/799.champagne-tower.md) 🆕
191+
- [0801. 使序列递增的最小交换次数](./problems/801.minimum-swaps-to-make-sequences-increasing.md) 🆕
191192
- [0816. 模糊坐标](./problems/816.ambiguous-coordinates.md)
192193
- [0820. 单词的压缩编码](./problems/820.short-encoding-of-words.md)
193194
- [0875. 爱吃香蕉的珂珂](./problems/875.koko-eating-bananas.md)
Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
## 题目地址(801. 使序列递增的最小交换次数)
2+
3+
https://leetcode-cn.com/problems/minimum-swaps-to-make-sequences-increasing/
4+
5+
## 题目描述
6+
7+
```
8+
我们有两个长度相等且不为空的整型数组 A 和 B 。
9+
10+
我们可以交换 A[i] 和 B[i] 的元素。注意这两个元素在各自的序列中应该处于相同的位置。
11+
12+
在交换过一些元素之后,数组 A 和 B 都应该是严格递增的(数组严格递增的条件仅为A[0] < A[1] < A[2] < ... < A[A.length - 1])。
13+
14+
给定数组 A 和 B ,请返回使得两个数组均保持严格递增状态的最小交换次数。假设给定的输入总是有效的。
15+
16+
示例:
17+
输入: A = [1,3,5,4], B = [1,2,3,7]
18+
输出: 1
19+
解释:
20+
交换 A[3] 和 B[3] 后,两个数组如下:
21+
A = [1, 3, 5, 7] , B = [1, 2, 3, 4]
22+
两个数组均为严格递增的。
23+
24+
注意:
25+
26+
A, B 两个数组的长度总是相等的,且长度的范围为 [1, 1000]。
27+
A[i], B[i] 均为 [0, 2000]区间内的整数。
28+
```
29+
30+
## 前置知识
31+
32+
- 动态规划
33+
34+
## 公司
35+
36+
- 暂无
37+
38+
## 思路
39+
40+
要想解决这道题,需要搞定两个关键点。
41+
42+
### 关键点一:无需考虑全部整体,而只需要考虑相邻两个数字即可
43+
44+
这其实也是可以使用动态规划解决问题的关键条件。对于这道题来说,**最小**的子问题就是当前项和前一项组成的局部,**无法**再小了,**没有必要**再大了。
45+
46+
为什么只关心两个数字即可?因为要使得整个数组递增,**假设**前面的 i - 2 项已经满足递增了,那么现在**采取某种方式**使得满足 A[i] > A[i-1] 即可(B 也是同理)。
47+
48+
> 因为 A[i - 1] > A[i-2] 已经成立,因此如果 A[i] > A[i - 1],那么整体就递增了。
49+
50+
这提示我们可以使用动态规划来完成。 如果上面的这些没有听懂,则很有可能对动态规划不熟悉,建议先看下基础知识。
51+
52+
### 关键点二:相邻两个数字的大小关系有哪些?
53+
54+
由于题目一定有解,因此交换相邻项中的**一个或两个**一定能满足**两个数组都递增**的条件。换句话说,如下的情况是不可能存在的:
55+
56+
```
57+
A:[1,2,4]
58+
B:[1,5,1]
59+
```
60+
61+
因为无论怎么交换都无法得到两个递增的序列。那相邻数字的大小关系究竟有哪些呢?实际上大小关系一共有四种。为了描述方便,先列举两个条件,之后直接用 q1 和 q2 来引用这两个关系。
62+
63+
```
64+
q1:A[i-1] < A[i] and B[i-1] < B[i]
65+
q2:A[i-1] < B[i] and B[i-1] < A[i]
66+
```
67+
68+
- q1 表示的是两个数组本身就已经递增了,你**可以选择**不交换。
69+
- q2 表示的是两个数组必须进行一次交换,你**可以选择**交换 i 或者交换 i - 1。
70+
71+
铺垫已经有了,接下来我们来看下这四种关系。
72+
73+
关系一:q1 满足 q2 满足。换不换都行,换 i 或者 i - 1 都行, 也可以都换
74+
75+
关系二:q1 不满足 q2 不满足。无解,对应上面我举的不可能存在的情况
76+
77+
关系三:q1 满足 q2 不满足。换不换都行,但是如果换需要都换。
78+
79+
关系四:q1 不满足 q2 满足 。必须换,换 i 或者 i - 1
80+
81+
接下来按照上面的四种关系进行模拟即可解决。
82+
83+
## 关键点
84+
85+
- 无需考虑全部整体,而只需要考虑相邻两个数字即可
86+
- 分情况讨论
87+
- 从题目的**一定有解**条件入手
88+
89+
## 代码
90+
91+
- 语言支持:Python3
92+
93+
Python3 Code:
94+
95+
```py
96+
97+
class Solution:
98+
def minSwap(self, A: List[int], B: List[int]) -> int:
99+
n = len(A)
100+
swap = [n] * n
101+
no_swap = [n] * n
102+
swap[0] = 1
103+
no_swap[0] = 0
104+
105+
for i in range(1, len(A)):
106+
q1 = A[i-1] < A[i] and B[i-1] < B[i]
107+
q2 = A[i-1] < B[i] and B[i-1] < A[i]
108+
if q1 and q2:
109+
no_swap[i] = min(swap[i-1], no_swap[i-1]) # 都不换或者换i-1
110+
swap[i] = min(swap[i-1], no_swap[i-1]) + 1 # 都换 或者 换 i
111+
if q1 and not q2:
112+
swap[i] = swap[i-1] + 1 # 都换
113+
no_swap[i] = no_swap[i-1] # 都不换
114+
if not q1 and q2:
115+
swap[i] = no_swap[i-1] + 1 # 换 i
116+
no_swap[i] = swap[i-1] # 换 i - 1
117+
118+
return min(swap[n-1], no_swap[n-1])
119+
```
120+
121+
实际上,我们也可以将逻辑进行合并,这样代码更加简洁。力扣中国题解区很多都是这种写法。即:
122+
123+
```py
124+
if q1:
125+
no_swap[i] = no_swap[i-1] # 都不换
126+
swap[i] = swap[i-1] + 1 # 都换
127+
if q2:
128+
swap[i] = min(swap[i], no_swap[i-1] + 1) # 换 i
129+
no_swap[i] = min(no_swap[i], swap[i-1]) # 换 i - 1
130+
```
131+
132+
可以看出,这种写法和上面逻辑是一致的。
133+
134+
逻辑合并之后的代码,更简短。但由于两个分支可能都执行到,因此不太容易直接写出。
135+
136+
代码:
137+
138+
```py
139+
class Solution:
140+
def minSwap(self, A: List[int], B: List[int]) -> int:
141+
n = len(A)
142+
swap = [n] * n
143+
no_swap = [n] * n
144+
swap[0] = 1
145+
no_swap[0] = 0
146+
147+
for i in range(1, len(A)):
148+
# 如果交换之前有序,则可以不交换
149+
if A[i-1] < A[i] and B[i-1] < B[i]:
150+
no_swap[i] = no_swap[i-1]
151+
swap[i] = swap[i-1] + 1
152+
# 否则至少需要交换一次(交换当前项或者前一项)
153+
if A[i-1] < B[i] and B[i-1] < A[i]:
154+
swap[i] = min(swap[i], no_swap[i-1] + 1) # i 换
155+
no_swap[i] = min(no_swap[i], swap[i-1]) # i - 1 换
156+
157+
return min(swap[n-1], no_swap[n-1])
158+
```
159+
160+
**复杂度分析**
161+
162+
令 n 为数组长度。
163+
164+
- 时间复杂度:$O(n)$
165+
- 空间复杂度:$O(n)$
166+
167+
> 此题解由 [力扣刷题插件](https://leetcode-pp.github.io/leetcode-cheat/?tab=solution-template) 自动生成。
168+
169+
力扣的小伙伴可以[关注我](https://leetcode-cn.com/u/fe-lucifer/),这样就会第一时间收到我的动态啦~
170+
171+
以上就是本文的全部内容了。大家对此有何看法,欢迎给我留言,我有时间都会一一查看回答。更多算法套路可以访问我的 LeetCode 题解仓库:https://github.com/azl397985856/leetcode 。 目前已经 40K star 啦。大家也可以关注我的公众号《力扣加加》带你啃下算法这块硬骨头。
172+
173+
关注公众号力扣加加,努力用清晰直白的语言还原解题思路,并且有大量图解,手把手教你识别套路,高效刷题。
174+
175+
![](https://tva1.sinaimg.cn/large/007S8ZIlly1gfcuzagjalj30p00dwabs.jpg)

0 commit comments

Comments
 (0)