Leetcode.1040 移动石子直到连续 II

题目链接

Leetcode.1040 移动石子直到连续 II Rating : 2456

题目描述

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

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

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

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

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

示例 1:

输入:[7,4,9]
输出:[1,2]
解释:
我们可以移动一次,4 -> 8,游戏结束。
或者,我们可以移动两次 9 -> 5,4 -> 6,游戏结束。

示例 2:

输入:[6,5,4,3,10]
输出:[2,3]
解释:
我们可以移动 3 -> 8,接着是 10 -> 7,游戏结束。
或者,我们可以移动 3 -> 7, 4 -> 8, 5 -> 9,游戏结束。
注意,我们无法进行 10 -> 2 这样的移动来结束游戏,因为这是不合要求的移动。

示例 3:

输入:[100,101,104,102,103]
输出:[0,0]

提示:
  • 3 < = s t o n e s . l e n g t h < = 1 0 4 3 <= stones.length <= 10^4 3<=stones.length<=104
  • 1 < = s t o n e s [ i ] < = 1 0 9 1 <= stones[i] <= 10^9 1<=stones[i]<=109
  • stones[i]的值各不相同。

解法:排序 + 滑动窗口

  • 一个 端点 移动之后,不能还是 端点

  • 当端点移动之后,整个区间的范围必然会缩小。

  • 当不能移动的时候,整个区间必然有序。

最大移动次数:

由于每次移动端点,都会缩减区间长度。为了使 移动次数最大,我们考虑让 每次区间缩减的长度尽可能的小,比如每次都缩减 1

左端点向右边的空闲位置移动:

在这里插入图片描述

右端点向左边的空闲位置移动:

在这里插入图片描述

所以两者取最大值,即最大的移动次数为 m a x { 11 , 12 } = 12 max\{11 , 12 \} = 12 max{11,12}=12

  • 右边的空闲位置数量 d 1 = s [ n − 1 ] − s [ 1 ] − n + 2 d1 = s[n-1] - s[1] - n + 2 d1=s[n1]s[1]n+2
  • 左边的空闲位置数量 d 2 = s [ n − 2 ] − s [ 0 ] − n + 2 d2 = s[n-2] - s[0] - n + 2 d2=s[n2]s[0]n+2
  • 最大移动次数 m x = m a x { d 1 , d 2 } mx = max \{ d1 , d2 \} mx=max{d1,d2}

最小移动次数:

特殊情况:

  • 如果左边 或者 右边的空闲位置为 0 ,也就是左边 或者 右边的所有点都 连续,即 d 1 = 0 ∣ ∣ d 2 = 0 d1 = 0 || d2 = 0 d1=0∣∣d2=0,最小移动次数就为 m i = m i n { 2 , m x } mi = min \{ 2 , mx \} mi=min{2,mx}

一般情况:

stonessizen。因为到最后必然是 n个连续的数(就是 d = 1 d = 1 d=1 的等差数列),所以我们只需要维护一个长度为 n的窗口,更新不超过窗口长度 n的最大元素个数 max_count

所以,最小的移动次数就为 m i = n − m a x _ c o u n t mi = n - max\_count mi=nmax_count

最后返回 { m i , m x } \{ mi , mx \} {mi,mx}即可。

时间复杂度: O ( n l o g n ) O(nlogn) O(nlogn)

C++代码:

class Solution {
public:
    vector<int> numMovesStonesII(vector<int>& s) {
        sort(s.begin(),s.end());
        int n  = s.size();

        int p1 = s[n - 2] - s[0] - n + 2 , p2 = s[n - 1] - s[1] - n + 2;
        int mx = max(p1,p2);
        
        if(p1 == 0 || p2 == 0){
            return {min(2 , mx) , mx};
        }

        int max_count = 0;
        for(int i = 0,j = 0;j < n;j++){
            while(s[j] - s[i] + 1 > n){
                i++;
            }

            max_count = max(max_count , j - i + 1);
        }

        int mi = n - max_count;

        return {mi,mx};
    }
};


Python代码:

class Solution:
    def numMovesStonesII(self, s: List[int]) -> List[int]:
        s.sort()
        n = len (s)

        d1 = s[-2] - s[0] - n + 2
        d2 = s[-1] - s[1] - n + 2

        mx = max(d1,d2)

        if d1 == 0 or d2 == 0:
            return [min(2 , mx) , mx]

        max_count , i = 0 , 0
        for j in range(0,n):
            while s[j] - s[i] + 1 > n:
                i = i + 1

            max_count = max(max_count,j - i + 1)

        mi = n - max_count

        return [mi,mx]


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值