力扣 801. 使序列递增的最小交换次数

题目来源:https://leetcode.cn/problems/minimum-swaps-to-make-sequences-increasing/

大致题意:
给两个数组 nums1 和 nums2,一次交换操作可以交换相同索引处两个数组的元素,求最少需要交换多少次可以使两个数组都递增

如 [1, 5, 4, 8] 和 [2, 3, 6, 9],可以交换索引 2 处元素,使两个数组变为 [1, 5, 6, 8] 和 [2, 3, 4, 9],这样只交换一次两个数组都满足递增

题目保证两个数组能在交换一定次数后都递增

思路

首先分析两个数组当前位置元素与上一个位置元素的关系,有以下几种

  1. nums1[i] > nums1[i - 1] 且 nums2[i] > nums2[i - 1],这种情况不需要交换即满足递增。如果上一位交换了,那么当前位也需要交换
  2. nums1[i] > nums1[i - 1] 且 nums2[i] > nums2[i - 1],这种情况需要交换当前位才满足递增,如果上一位交换了,那么当前位不需要交换
  3. nums1[i] > nums1[i - 1] 且 nums2[i] > nums2[i - 1],同时 nums1[i] > nums1[i - 1],nums2[i] > nums2[i - 1],这种情况不管上一位交换与否都满足递增

于是可以使用两个标志位 unChange 和 change 表示上一位不交换和交换满足递增所需的最小次数

那么对于上述三种情况(第三种情况包含在前两种的判断中),unChange 和 change 的更新方法为

  1. 使用 unChangeTemp 和 changeTemp,表示本轮操作后不交换和交换满足递增所需的最小次数,初始设置为最大值 n
  2. 首先判断如果满足第一个条件,那么 unChangeTemp 即为上一轮不交换的次数,changeTemp 更新为上一轮交换次数 + 1
  3. 之后判断如果满足第二个条件,那么 unChangeTemp 为当前值与上一轮交换次数的最小值(因为当前值可能在上一个判断中更新,即考虑第三个条件,两种情况都满足时的判断),changeTemp 更新为上一轮不交换次数 + 1 与当前值的最小值 (因为当前值可能在上一个判断中更新,即考虑第三个条件,两种情况都满足时的判断)
  4. 然后根据 unChangeTemp 和 changeTemp 更新标志位 unChange 和 change

最终返回标志位 unChange 和 change 的最小值即可

具体看代码:

class Solution {
    public int minSwap(int[] nums1, int[] nums2) {
        int n = nums1.length;
        int unChange = 0;
        int change = 1;   // 初始时,交换首位元素,操作次数为 1
        for (int i = 1; i < n; i++) {
            // 初始化当前位递增的最小交换次数
            int unChangeTemp = n;
            int changeTemp = n;
            // 如果不交换就满足递增
            if (nums1[i] > nums1[i - 1] && nums2[i] > nums2[i - 1]) {
                // 当前位不交换就满足递增
                // 当前位不交换的操作次数与上一轮相同
                unChangeTemp = unChange;
                // 上一位交换了,当前位交换才能满足递增,操作次数为上一轮交换次数 + 1
                changeTemp = change + 1;
            }
            // 如果交换后才满足递增
            if (nums1[i] > nums2[i - 1] && nums2[i] > nums1[i - 1]) {
                // 当前位必须交换,或者上一位交换才满足递增
                // 当前位不交换,上一位交换
                unChangeTemp = Math.min(unChangeTemp, change);
                // 当前位交换,上一位不交换
                changeTemp = Math.min(unChange + 1, changeTemp);
            }
            change = changeTemp;
            unChange = unChangeTemp;
        }
        return Math.min(change, unChange);
    }
}
### 解决方案概述 对于LeetCode 1584题——连接所有点的最小费用,目标是在给定平面上的一组点之间建立边,使得这些点全部连通,并且总成本最低。此问题可以通过构建最小生成树(Minimum Spanning Tree, MST)[^2]来解决。 #### Prim's Algorithm 实现方法 Prim’s算法是一种用于求解无向图中最小生成树的有效贪心算法。该算法通过逐步扩展已有的部分生成树直到覆盖所有的顶点为止,在每一步都选择当前未加入的部分中最短的边。 ```python import heapq def minCostConnectPoints(points): n = len(points) # 计算曼哈顿距离作为权重函数 def manhattan(p1, p2): return abs(p1[0]-p2[0]) + abs(p1[1]-p2[1]) visited = set() heap = [(0, 0)] # (cost, point_index) result = 0 while len(visited) < n: cost, i = heapq.heappop(heap) if i in visited: continue visited.add(i) result += cost for j in range(n): if j not in visited and j != i: heapq.heappush(heap, (manhattan(points[i], points[j]), j)) return result ``` 上述代码实现了基于优先队列优化版本的Prim’s算法。首先定义了一个辅助函数`manhattan()`用来计算两点之间的曼哈顿距离;接着初始化一个小根堆存储候选节点及其对应的代价;最后进入循环直至访问过所有节点并累加路径长度得到最终的结果。 #### Kruskal's Algorithm 实现方式 Kruskal’s算法也是一种常用的MST算法,它按照从小到大的顺序处理各条边,只当一条边不会形成环路时才将其添加至正在形成的森林里。为了高效实现这一点,可以采用Union-Find数据结构来进行动态集合操作。 ```python class UnionFind(object): def __init__(self, size): self.parent = list(range(size)) def find(self, x): if self.parent[x] != x: self.parent[x] = self.find(self.parent[x]) return self.parent[x] def union(self, u, v): rootU = self.find(u) rootV = self.find(v) if rootU == rootV: return False else: self.parent[rootU] = rootV return True def minCostConnectPoints_kruskal(points): edges = [] n = len(points) for i in range(n): for j in range(i+1, n): distance = abs(points[i][0] - points[j][0]) + \ abs(points[i][1] - points[j][1]) edges.append((distance, i, j)) uf = UnionFind(n) edges.sort() res = 0 count = 0 for dist, u, v in edges: if uf.union(u, v): res += dist count += 1 if count >= n-1: break return res ``` 这段Python程序展示了如何利用Kruskal的方法解决问题。创建了名为`edges`列表保存所有可能存在的边以及它们各自的权值(即两个端点间的曼哈特尼距离),之后对其进行升序排列。随后遍历排序后的边集尝试将符合条件的新边纳入结果集中去,同时维护一个计数器确保恰好选择了\(n−1\)条独立边构成一棵完整的树形结构。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

三更鬼

谢谢老板!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值