题目解析:石子移动问题 | 豆包MarsCode AI刷题

155 阅读3分钟

问题描述

小S正在玩一个关于石子的游戏,给定了一些石子,它们位于一维数轴的不同位置,位置用数组 stones 表示。如果某个石子处于最小或最大的一个位置,我们称其为端点石子

在每个回合,小S可以将一颗端点石子移动到一个未占用的位置,使其不再是端点石子。游戏继续,直到石子的位置变得连续,无法再进行任何移动操作。

你需要帮助小S找到可以移动的最大次数。


测试样例

样例1:

输入:stones = [7, 4, 9]
输出:2

样例2:

输入:stones = [6, 5, 4, 3, 10]
输出:3

样例3:

输入:stones = [1, 2, 3, 4, 5]
输出:0

思路分析

首先分析题意,在每个回合,要拿起一个端点石子,产生一个新的端点石子,然后把拿起的石子放在新的两个端点石子中间的未占用位置。

我们想要找到可以移动的最大次数,那我们就要尽可能地利用所有未占用的位置。可以发现,只要一个端点石子与另一个石子相邻或在一个连续石子序列中,那么这个端点石子可以从连续石子序列的一端移动到序列的另一端,这样重复操作,就可以利用所有的未占用位置。

但是还有一个问题,就是刚开始可能两个端点都没有相邻石子,此时我们需要先移动一次才能满足上面的情况,并完全利用剩下的所有未占用位置。也就是说,左端点石子与第二个石子之间的未占用位置和右端点石子与倒数第二个石子之间的未占用位置必定有一个要浪费掉,那么我们就只能浪费掉其中的最小值。

代码实现

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

int solution(vector<int>& stones) {
    int n = stones.size();
    if(n < 3) return 0;

    vector<int> sorted_stones(stones);
    sort(sorted_stones.begin(), sorted_stones.end());

    return sorted_stones[n-1] - sorted_stones[0] + 1 - n - min(sorted_stones[1] - sorted_stones[0] - 1, sorted_stones[n-1] - sorted_stones[n-2] - 1);
}

首先,如果 n < 3 ,无法移动石子,直接返回 0

然后将石子进行排序,使其按位置从左到右排列。

左端点石子的位置是 sorted_stones[0] ,右端点石子的位置是 sorted_stones[n-1] ,那么 sorted_stones[n-1] - sorted_stones[0] + 1 就可以得到两个端点石子之间的所有位置(包括两个端点石子),再减去石子数量 n 就得到了两个端点石子之间的未占用位置。

此时,还要考虑初次移动的损失。 sorted_stones[1] - sorted_stones[0] - 1 是左端点石子与第二个石子之间的未占用位置, sorted_stones[n-1] - sorted_stones[n-2] - 1 是右端点石子与倒数第二个石子之间的未占用位置,减去两者的最小值即可。

复杂度分析

由于用了排序算法,时间复杂度大概是 O(nlogn)O(nlog_n) ,实际上有 O(n)O(n) 的算法,因为只要找出最大的两个值和最小的两个值就可以了,但是代码会稍微复杂一些,所以这里暂时先不用。