【每日一题Day170】LC1040移动石子直到连续 II | 双指针 贪心 数学

44 篇文章 0 订阅
23 篇文章 0 订阅

移动石子直到连续 II【LC1040】

在一个长度 无限 的数轴上,第 i 颗石子的位置为 stones[i]。如果一颗石子的位置最小/最大,那么该石子被称作 端点石子

每个回合,你可以将一颗端点石子拿起并移动到一个未占用的位置,使得该石子不再是一颗端点石子。

值得注意的是,如果石子像 stones = [1,2,5] 这样,你将 无法 移动位于位置 5 的端点石子,因为无论将它移动到任何位置(例如 0 或 3),该石子都仍然会是端点石子。

当你无法进行任何移动时,即,这些石子的位置连续时,游戏结束。

要使游戏结束,你可以执行的最小和最大移动次数分别是多少? 以长度为 2 的数组形式返回答案:answer = [minimum_moves, maximum_moves]

这几天一直在干活 然后昨天下午做题一直被打断 最后我都以为自己已经发好题解了 结果并没有发…

  • 思路

    • 首先,明确每次移动时只能移动端点,并且端点移动后不能还是端点,即移动后石子的左侧和右侧必须有其他石子。每次移动后两边端点的距离较小

    • 什么时候无法移动?

      所有石子之间没有空位,都位于连续的n个位置

    • 如何获得最大移动次数?

      每次移动,都让移动的距离尽可能小【贪心】,那么移动次数就能达到最大。

      那么第一次移动存在两种移动方式

      • 移动stones[0]stones[1]+1
      • 移动stones[n-1]stones[n-2]-1

      而我们之后的每次移动都可以使移动距离为1,从而获得最大的移动次数,移动的次数取决于第一次移动后新端点之间的空位,即

      • s [ n − 1 ] − s [ 1 ] − 1 − ( n − 3 ) = s [ n − 1 ] − s [ 1 ] − n + 2 s[n-1]-s[1]-1-(n-3)=s[n-1]-s[1]-n+2 s[n1]s[1]1(n3)=s[n1]s[1]n+2
      • s [ n − 2 ] − s [ 0 ] − 1 − ( n − 3 ) = s [ n − 2 ] − s [ 0 ] − n + 2 s[n-2]-s[0]-1-(n-3)=s[n-2]-s[0]-n+2 s[n2]s[0]1(n3)=s[n2]s[0]n+2

      因此,最大移动次数为
      m a x ( s [ n − 1 ] − s [ 1 ] − n + 2 , s [ n − 2 ] − s [ 0 ] − n + 2 ) max(s[n-1]-s[1]-n+2,s[n-2]-s[0]-n+2) max(s[n1]s[1]n+2,s[n2]s[0]n+2)

    • 如何获得最小移动次数?

      由于所有端点可以移动至可以中间任意的空位,那么我们应该尽可能将石子移动至最合适的问题。

      那么我们可以枚举长度为 n n n的滑动窗口,计算其中没有石子的位置的个数,取最小值返回。

      但此时存在特殊情况,即移动后石子仍为端点的情况image-20230407162616278

      于是,我们需要判断, s [ 0 ] 至 s [ n − 2 ] s[0]至s[n-2] s[0]s[n2] s [ 1 ] 至 s [ n − 1 ] s[1]至s[n-1] s[1]s[n1]之间是否有空位,如果没有空位,那么答案为2;还需比较与最大移动次数的关系,如果最小移动次数应小于最大移动次数

      • 滑动窗口
  • 实现

    class Solution {
        public int[] numMovesStonesII(int[] s) {
            Arrays.sort(s);
            int n = s.length;
            int e1 = s[n - 2] - s[0] - n + 2;
            int e2 = s[n - 1] - s[1] - n + 2; // 计算空位
            int maxMove = Math.max(e1, e2);
            if (e1 == 0 || e2 == 0) // 特殊情况:没有空位
                return new int[]{Math.min(2, maxMove), maxMove};
            int maxCnt = 0, left = 0;
            for (int right = 0; right < n; ++right) { // 滑动窗口:枚举右端点所在石子
                while (s[right] - s[left] + 1 > n) // 窗口长度大于 n
                    ++left; // 缩小窗口长度
                maxCnt = Math.max(maxCnt, right - left + 1); // 维护窗口内的最大石子数
            }
            return new int[]{n - maxCnt, maxMove};
        }
    }
    
    作者:灵茶山艾府
    链接:https://leetcode.cn/problems/moving-stones-until-consecutive-ii/solutions/2212638/tu-jie-xia-tiao-qi-pythonjavacgo-by-endl-r1eb/
    来源:力扣(LeetCode)
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
    
    • 复杂度
      • 时间复杂度: O ( n l o g n ) O(nlogn) O(nlogn)
      • 空间复杂度: O ( 1 ) O(1) O(1)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值