得到连续 K 个 1 的最少相邻交换次数【LC1703】
You are given an integer array,
nums, and an integerk.numscomprises of only0’s and1’s. In one move, you can choose two adjacent indices and swap their values.Return the minimum number of moves required so that
numshaskconsecutive1’s.
太难了 终于想明白了
-
思路:我们需要将数组中的1移动成连续的k的1,并计算最少的移动次数,每次移动只能移动相邻的两个元素。假设第 i i i个1的下标为 q i q_i qi,我们需要将第一个1 q 0 q_0 q0移动至位置 x x x,并移动第二个1 q 1 q_1 q1至 x + 1 x+1 x+1,所需要的移动次数总和为
∑ i i + k − 1 ∣ q i − ( x + i ) ∣ = ∑ i i + k − 1 ∣ ( q i − i ) − x ∣ = ∑ i i + k − 1 ∣ p i − x ∣ \sum_{i}^{i+k-1}|q_i-(x+i)|=\sum_{i}^{i+k-1}|(q_i-i)-x|=\sum_{i}^{i+k-1}|p_i-x| i∑i+k−1∣qi−(x+i)∣=i∑i+k−1∣(qi−i)−x∣=i∑i+k−1∣pi−x∣
因此问题转变为计算所有 p i p_i pi至 x x x的距离和的最小值,贪心可得当 x x x取 p i p_i pi的中位数 p [ ( 2 i + k − 1 ) / 2 ] p[(2i+k-1)/2] p[(2i+k−1)/2]时,距离之和最小。-
为什么?
局部最优: x x x位于区间 [ p [ 0 ] , p [ k − 1 ] ] [p[0],p[k-1]] [p[0],p[k−1]]内部时【无论位于哪个位置】,到 p [ 0 ] p[0] p[0]和 p [ k − 1 ] p[k-1] p[k−1]的距离是定值 p [ k − 1 ] − p [ 0 ] p[k-1]-p[0] p[k−1]−p[0],因此应使 x x x位于所有区间的内部即中位数,使 x x x到所有区间的距离为定值,此时能够达到全局最优
全局最优:所有 p i p_i pi至 x x x的距离和的最小值
-
令中位数为 m i d = ( 2 i + k − 1 ) / 2 mid=(2i+k-1)/2 mid=(2i+k−1)/2,那么此时的距离可计算得
∑ i k − 1 ∣ p i − x ∣ = ∑ j = i m i d − 1 ( p m i d − p j ) + ∑ j = m i d + 1 i + k − 1 ( p j − p m i d ) = ( m i d − i ) p m i d − ∑ j = i m i d − 1 p j + ∑ j = m i d + 1 i + k − 1 p j − − ( i + k − 1 − m i d ) p m i d = ( 2 ( m i d − i ) − k + 1 ) p m i d + ∑ j = m i d + 1 i + k − 1 p j − ∑ j = i m i d − 1 p j \sum_{i}^{k-1}|p_i-x|= \sum_{j=i}^{mid-1}(p_{mid}-p_j)+\sum_{j=mid+1}^{i+k-1}(p_j-p_{mid})\\ =(mid-i)p_{mid}-\sum_{j=i}^{mid-1}p_j+\sum_{j=mid+1}^{i+k-1}p_j--(i+k-1-mid)p_{mid}\\ =(2(mid-i)-k+1)p_{mid}+\sum_{j=mid+1}^{i+k-1}p_j-\sum_{j=i}^{mid-1}p_j i∑k−1∣pi−x∣=j=i∑mid−1(pmid−pj)+j=mid+1∑i+k−1(pj−pmid)=(mid−i)pmid−j=i∑mid−1pj+j=mid+1∑i+k−1pj−−(i+k−1−mid)pmid=(2(mid−i)−k+1)pmid+j=mid+1∑i+k−1pj−j=i∑mid−1pj -
枚举第 i i i个1作为连续1的最左边那个1,求出此时的距离,返回最小距离即可
-
-
实现
- 找到所有1的位置存储在List类型的变量
loc中 - 根据
loc求出数组p - 求出
p的前缀和数组pSum - 枚举最左边的1 求出最小值
class Solution { public int minMoves(int[] nums, int k) { int n = nums.length; List<Integer> loc = new ArrayList<>(); // 找到数组中1的位置 for (int i = 0; i < n; i++){ if (nums[i] == 1){ loc.add(i); } } // 求出p数组 int m = loc.size(); int[] p = new int[m]; for (int i = 0; i < m; i++){ p[i] = loc.get(i) - i; } // 前缀和数组 int[] pSum = new int[m + 1]; for (int i = 1; i <= m; i++){ pSum[i] = pSum[i - 1] + p[i - 1]; } // 枚举左边的1 求出最小值 int ans = Integer.MAX_VALUE; for (int i = 0; i + k <= m; i++){ int mid = (2 * i + k - 1) / 2; int temp = (2 * (mid - i) - k + 1) * p[mid] + pSum[i + k] - pSum[mid + 1] - (pSum[mid] - pSum[i]); ans = Math.min(ans, temp); } return ans; } }- 复杂度
- 时间复杂度: O ( n + m ) O(n+m) O(n+m), n n n为数组长度, m m m为数组中1的个数
- 空间复杂度: O ( n + m ) O(n+m) O(n+m)
- 找到所有1的位置存储在List类型的变量
-
优化:直接求出数组p
class Solution { public int minMoves(int[] nums, int k) { int n = nums.length; List<Integer> p = new ArrayList<>(); // 求出p数组 for (int i = 0; i < n; i++){ if (nums[i] == 1){ p.add(i - p.size() ); } } int m = p.size(); // 前缀和数组 int[] pSum = new int[m + 1]; for (int i = 1; i <= m; i++){ pSum[i] = pSum[i - 1] + p.get(i - 1); } // 枚举左边的1 求出最小值 int ans = Integer.MAX_VALUE; for (int i = 0; i + k <= m; i++){ int mid = (2 * i + k - 1) / 2; int temp = (2 * (mid - i) - k + 1) * p.get(mid) + pSum[i + k] - pSum[mid + 1] - (pSum[mid] - pSum[i]); ans = Math.min(ans, temp); } return ans; } }

本文介绍了一种解决LeetCode 1703问题的方法,通过贪心策略计算将0-1数组转换为连续k个1所需的最少相邻交换次数。关键步骤包括构造p数组、计算前缀和并遍历可能的起始位置。优化版本简化了p数组的计算过程。

被折叠的 条评论
为什么被折叠?



