【每日一题Day60】LC1703得到连续 K 个 1 的最少相邻交换次数 | 中位数贪心

简介: 局部最优:x位于区间[p[0],p[k−1]]内部时【无论位于哪个位置】,到p[0]和p[k−1]的距离是定值p[k−1]−p[0],因此应使x xx位于所有区间的内部即中位数,使x xx到所有区间的距离为定值,此时能够达到全局最优

得到连续 K 个 1 的最少相邻交换次数【LC1703】


You are given an integer array, nums, and an integer k. nums comprises of only 0’s and 1’s. In one move, you can choose two adjacent indices and swap their values.


Return the minimum number of moves required so that nums has k consecutive 1’s.


太难了 终于想明白了


  • 思路:我们需要将数组中的1移动成连续的k的1,并计算最少的移动次数,每次移动只能移动相邻的两个元素。假设第i个1的下标为q i ,我们需要将第一个1q 0 移动至位置x,并移动第二个1q 1  至x+1,所需要的移动次数总和为


image.png


因此问题转变为计算所有p i 至x的距离和的最小值,贪心可得当x取p i 的中位数p[(2i+k−1)/2]时,距离之和最小。


。为什么?


局部最优:x位于区间[p[0],p[k−1]]内部时【无论位于哪个位置】,到p[0]和p[k−1]的距离是定值p[k−1]−p[0],因此应使x xx位于所有区间的内部即中位数,使x xx到所有区间的距离为定值,此时能够达到全局最优


全局最优:所有p i  至x 的距离和的最小值


。令中位数为mid=(2i+k−1)/2,那么此时的距离可计算得


image.png


。枚举第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),n为数组长度,m 为数组中1的个数
  • 空间复杂度:O(n+m)


  • 优化:直接求出数组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;
    }
}
目录
相关文章
【动态规划刷题 14】最长递增子序列的个数&& 最长数对链
【动态规划刷题 14】最长递增子序列的个数&& 最长数对链
|
3月前
|
算法 Java
LeetCode初级算法题:子数组最大平均数+二叉树的最小深度+最长连续递增序列+柠檬水找零
LeetCode初级算法题:子数组最大平均数+二叉树的最小深度+最长连续递增序列+柠檬水找零
43 0
|
5月前
|
算法 测试技术 程序员
力扣经典150题第三十题:长度最小的子数组
力扣经典150题第三十题:长度最小的子数组
29 1
|
5月前
【LeetCode刷题】滑动窗口思想解决:最大连续1的个数 III、将x减到0的最小操作数
【LeetCode刷题】滑动窗口思想解决:最大连续1的个数 III、将x减到0的最小操作数
|
6月前
|
算法 测试技术 C++
【动态规划】【C++算法】801. 使序列递增的最小交换次数
【动态规划】【C++算法】801. 使序列递增的最小交换次数
|
6月前
|
vr&ar
【每日一题Day166】LC1053交换一次的先前排列 | 贪心
【每日一题Day166】LC1053交换一次的先前排列 | 贪心
72 1
|
6月前
|
存储
【每日一题Day276】LC2208将数组和减半的最少操作次数 | 贪心+大顶堆
【每日一题Day276】LC2208将数组和减半的最少操作次数 | 贪心+大顶堆
43 0
|
6月前
【每日一题Day192】LC1033移动石子直到连续 | 分类讨论 贪心
【每日一题Day192】LC1033移动石子直到连续 | 分类讨论 贪心
32 0
|
算法 C++
剑指offer(C++)-JZ40:最小的K个数(算法-排序)
剑指offer(C++)-JZ40:最小的K个数(算法-排序)
|
Cloud Native Go
801. 使序列递增的最小交换次数:动态规划
这是 力扣上的 801. 使序列递增的最小交换次数,难度为 困难。