1040. 移动石子直到连续 II 思维题
思路
有补充的思路在代码注释中阐明。
代码
执行用时:16 ms, 在所有 C++ 提交中击败了95.45%的用户
内存消耗:11.9 MB, 在所有 C++ 提交中击败了59.09%的用户
class Solution {
public:
// 缩小左边端点和右边端点的距离
vector<int> numMovesStonesII(vector<int>& stones) {
int n=stones.size();
// n>=3
sort(stones.begin(),stones.end());
// 计算空白格子
int resMax=stones[n-1]-stones[0]+1-n;
resMax-=min(stones[n-1]-stones[n-2],stones[1]-stones[0])-1;
vector<int>res(2);
res[1]=resMax;
int resMin=0;
// 找到含有元素最多的长度为n的窗口
// 这个窗口的最左端或者最右端一定是一个stones[]中的石子所在的位置
// 假设这个窗口的最左端是一个石子,石子在left的位置,这个窗口最右边的位置是right
// 第一种情况,这个窗口的最右边的右边(>right的位置)有一个石子,那么窗口在left的位置肯定比在stones[left-1]的位置好
// 要是left-1的位置上有石子,这个窗口不如左移
// 第二种情况,这个窗口的最右边的右边(>right)没有石子了,但是right的位置有一个石子,这样和第一种情况类似
// 第三种情况,这个窗口的最右边的右边(>right)没有石子了,但是(<right)的位置pos有一个石子,这个石子是窗口内的最后一个石子,这样想要完成最小的移动,不如让窗口的最右端去对准pos这个地方的石子。
// 特殊情况,例如1,2,3,4,6,需要移动两次
// 结果,总元素数目-窗口内最多的元素数目
// 特殊情况需要移动两次
// 寻找最大的窗口
int num;
int maxNum=0; // 窗口内最大的元素的个数
res[0]=res[1]; // 初始化为最大值
for(int i=0;i<n;i++){
num=stones[i]+n-1;
int j=i+1;
while(j<n&&stones[j]<=num){ // 可以二分
j++;
}
maxNum=max(maxNum,j-i);
res[0]=min(n-maxNum,res[0]);
if(j-i==n-1&&stones[j-1]-stones[i]+1==n-1){ // 特殊情况处理
res[0]=2;
break;
}
}
res[0]=min(res[0],res[1]); // 可能结果为0,上面却break2出来了
return res;
}
};