高级农民
- 积分
- 4019
- 大米
- 颗
- 鳄梨
- 个
- 水井
- 尺
- 蓝莓
- 颗
- 萝卜
- 根
- 小米
- 粒
- 学分
- 个
- 注册时间
- 2017-1-22
- 最后登录
- 1970-1-1
|
LC. 1536. Minimum Swaps to Arrange a Binary Grid
Medium
Topics
conpanies icon
Companies
Hint
Given an n x n binary grid, in one step you can choose two adjacent rows of the grid and swap them.
A grid is said to be valid if all the cells above the main diagonal are zeros.
Return the minimum number of steps needed to make the grid valid, or -1 if the grid cannot be valid.
The main diagonal of a grid is the diagonal that starts at cell (1, 1) and ends at cell (n, n).
Example 1:
Input: grid = [[0,0,1],[1,1,0],[1,0,0]]
Output: 3
Example 2:
Input: grid = [[0,1,1,0],[0,1,1,0],[0,1,1,0],[0,1,1,0]]
Output: -1
Explanation: All rows are similar, swaps have no effect on the grid.
Example 3:
Input: grid = [[1,0,0],[1,1,0],[1,1,1]]
Output: 0
Constraints:
n == grid.length == grid[i].length
1 <= n <= 200
grid[i][j] is either 0 or 1
这道题目还真是没做出来,我想到了,可以用每行1的个数来确认它可以待在变换后矩阵的位置,但是没想到 greedy。
同时这题从结尾 0 的个数除法,比我的想法更好,更加直接。
1. 题目特征:一眼识破“贪心 + 冒泡”以后看到满足以下条件的题目,往这方面想:操作限制: 题目规定只能**“交换相邻元素”**(Adjacent Swaps)。目标状态: 最终要达成某种有序或特定的排列(比如对角线以上为 0,或者按大小排序)。最优化问题: 求**“最小步数”**。经验总结: > 在这类限制下,“最小步数”通常等于“逆序对”的数量,或者可以用贪心策略下的冒泡排序来解决。因为相邻交换不会产生“跨越式”的收益,你必须老老实实地把需要的那个元素“挪”过去。2. 算法模型:信息压缩与降维这道题最核心的突破点是:把二维矩阵压缩成一维数组。原始信息: 一个 $n \times n$ 的 $0/1$ 矩阵。冗余信息: 我们并不关心第 $i$ 行具体的 0 分布在左边还是中间。关键信息: 只关心末尾连续 0 的个数。经验总结:处理复杂数据结构(如矩阵、树、图)时,先问自己:“我到底需要哪些核心属性才能判断它是否合法?”。把 $n \times n$ 的复杂度通过预处理降维到长度为 $n$ 的数组,是解决矩阵类题目(如:LC 1275, LC 48)的常用套路。3. Python 面试技巧:优雅与效率的平衡你在上一问中担心的 pop 和 insert,其实是 Python 面试中的“加分项”:模拟物理过程: 用 pop(j) 弹出,用 insert(i, val) 插入,代码意图极其明显——“我要把这一行抽出来,插到前面去”。这比手写两个 for 循环做 swap 要简洁得多。性能边界: 虽然 pop 和 insert 是 $O(n)$,但这道题 $n=200$,整体 $O(n^2)$ 约为 $40,000$ 次运算。在面试中,代码的可读性和正确性远比在 $O(n^2)$ 内部斤斤计较那点常数项重要。4. 贪心策略的证明思路(面试话术)面试官可能会问:“你凭什么说先满足第 $i$ 行就是全局最优?”你可以这样回答(话术):“因为这道题对每一行的要求是单调递减的($n-1, n-2, \dots, 0$)。如果我们现在处理第 $i$ 行,面前有两个选择 A 和 B 都能满足当前这一行,但 A 在物理位置上离我们更近。如果选 A,当前步数最少。把较远的 B 留在后面,它更有可能满足后面更宽松的要求。反之,如果选了远的 B,不仅当前步数变多,还浪费了一个可能更好用的 A。”5. 易错坑点 Checklist以后做类似题,先在心里过一遍:无解判断: 是否存在无论怎么换都达不到要求的情况?(本题中:如果找不到满足 zeros[j] >= target 的行,直接返回 -1)。边界值: $n-1-i$ 这种下标计算,最好用 $i=0$ 或 $i=n-1$ 代入检查一下。贪心方向: 是从第一行开始匹配,还是从最后一行开始?(本题中:第一行最严格,先满足最严格的,剩下的才好安排)。- class Solution:
- def minSwaps(self, grid: List[List[int]]) -> int:
- # counting ending zeros
- # this is the finger prints of each row
- zeros = []
- n = len(grid)
- for row in grid:
- count = 0
- index = n - 1
- while index >= 0:
- if row[index] == 0:
- count += 1
- index -= 1
- else:
- break
- zeros.append(count)
- # 遍历 zeros,确定可以填充本行的最近的行 greedy
- # 对于第 i 行,需要找到的最近行的结尾zeros个数需要满足 >= n - 1 - i
- ans = 0
- for i in range(n):
- target = n - 1 - i
- ind = i
- foundTargetIndex = -1
- while ind < n:
- if zeros[ind] >= target:
- foundTargetIndex = ind
- break
- ind += 1
-
- if foundTargetIndex == -1:
- # no row match the required zeros
- return -1
- else:
- ans += (foundTargetIndex - i) # swap row foundTargetIndex to i
- # update zeros to make sure i + 1 has the new zeros layout
- # this is to simulate swapping
- targetZeroVals = zeros.pop(foundTargetIndex)
- zeros.insert(i, targetZeroVals)
-
- return ans
复制代码 |
|