1040. 移动石子直到连续 II

这是我的一个初始思理解与分析:
在这里插入图片描述
在这里插入图片描述
没有解出来。

看一下标准思路:
我们已经知道如何检测石子的最终状态是否符合题目要求。
求max:
石子最终状态即所有石子所占据的长度等于石子个数。若没有达到最终状态,那么石子所占据的长度一定是大于石子个数的。我们想要移动次数尽可能多,那么石子所占据的空间长度减小得需要尽可能慢。首先我们将石子排序并求出移动前石子们所占据的最大长度s1 = stones[n-1] - stones[0] + 1 - n。紧接着左边或者右边端点的石子被移动到剩余石子的间隙部分。如果我们想让移动后石子们占据的长度减小的最少,那么最左边的石子就需要移动到最左边第二颗石子的右边间隙,右边类似。(贪心思想: 如果最左边的放在了左边第三颗石子的右边,那么距离缩短的速度肯定会加快)通过检测左边和右边移动的距离哪个更小来优先移动哪个石子。对于移动距离(减少距离),我们有s2 = min { stones[1] - stones[0], stones[n-1] - stones[n-2] }。只有在第一步我们舍弃的空间是无法控制的,其余后面的步骤我们都可以只减少1步,也就是说max可以直接被计算为s1-s2

求min:
min就是最小步数内使石子连续,那我们就要找到已经排序的石子的最长连续n序列。找到序列之后再依次将外面的石子挪进去。注意,因为我们要将所有的外边的石子挪进这个序列中,所以至少要挪动n-(j-i+1)次。但是有一种例外,比如有一个序列1,2,3,4,7,此时1-4是最好的序列,但是我们却无法移动7,因为7无法插入1-2,2-3,3-4之间,而将7移动到0或5使得这颗石子依然是端头,所以只能将1挪到6,7挪到5。当只有一个石子在最边上的时候,都要花费两步才可以达成最终状态。

代码:(C++)

public:
    vector<int> numMovesStonesII(vector<int>& stones) {
    	// 排序
        sort(stones.begin(),stones.end());
        int n = stones.size();
        // s1
        int mx = stones[n - 1] - stones[0] + 1 - n;
        // max: s1 - s2
        mx -= min(stones[n-1]-stones[n-2] - 1, stones[1]-stones[0] -1);
        int mi = mx;
        int i = 0;
        int j = 0;
        for(i = 0; i < n; ++i)
        {
        	// 找到从i开始到j之间距离大于n的第一块石头
            while(j + 1 < n && stones[j + 1] - stones[i] + 1 <= n)
                ++j;
            // 计算剩余需要挪动的次数
            int cost = n - (j - i + 1);
            // 特殊状况:n-1个石头都是连续的,只剩了一个石头在端头
            if(j - i + 1 == n - 1 && stones[j] - stones[i] + 1 == n - 1)
                cost = 2;
            mi = min(mi, cost);
        }
        return vector<int>{mi, mx};
    }

很牛,真的很牛

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值