原题链接:力扣
在一个长度 无限 的数轴上,第 i
颗石子的位置为 stones[i]
。如果一颗石子的位置最小/最大,那么该石子被称作 端点石子 。
每个回合,你可以将一颗端点石子拿起并移动到一个未占用的位置,使得该石子不再是一颗端点石子。
值得注意的是,如果石子像 stones = [1,2,5]
这样,你将 无法 移动位于位置 5 的端点石子,因为无论将它移动到任何位置(例如 0 或 3),该石子都仍然会是端点石子。
当你无法进行任何移动时,即,这些石子的位置连续时,游戏结束。
要使游戏结束,你可以执行的最小和最大移动次数分别是多少? 以长度为 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]
注释版题解:
class Solution {
public:
vector<int> numMovesStonesII(vector<int>& stones) {
// 获取石头数量
int n = stones.size();
// 将石头按位置排序
sort(stones.begin(), stones.end());
// 如果石头形成一个连续的序列,那么不需要移动就可以完成游戏,返回结果{0,0}
if (stones.back() - stones[0] + 1 == n) {
return {0, 0};
}
// 找到移动石头的最大值和最小值
// 移动最大值就是将一侧的所有石头移动到另一侧,其中不需要移动的石头数量为 n - 1
// 因此,最大值是两侧剩余石头数量的较大值减去需要移动的石头数量 n - 1
int ma = max(stones[n - 2] - stones[0] + 1, stones[n - 1] - stones[1] + 1) - (n - 1);
// 移动最小值就是将剩余的石头尽可能地靠拢,形成最大的连续序列
int mi = n;
// 遍历石头,找到最长的连续子序列
for (int i = 0, j = 0; i < n && j + 1 < n; ++i) {
// 在当前位置 i 的情况下,找到最远的 j,使得区间 [i,j] 中的石头可以放到一起,形成连续的子序列
while (j + 1 < n && stones[j + 1] - stones[i] + 1 <= n) {
++j;
}
// 如果当前连续子序列的长度等于 n - 1,且两端的石头分别与序列的首尾相邻,那么只需要移动 2 个石头就可以形成连续的序列
if (j - i + 1 == n - 1 && stones[j] - stones[i] + 1 == n - 1) {
mi = min(mi, 2);
} else {
// 否则,需要移动 n - (j - i + 1) 个石头才能形成连续的序列
mi = min(mi, n - (j - i + 1));
}
}
// 返回最小值和最大值
return {mi, ma};
}
};