|
| 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 | + |
0 commit comments