题意
现在有一个数组,先不管什么叫轮调(k==0的情况),计算原数组的分数:一个数组中每个元素都有可能得分,得分的条件是元素的值小于等于其索引,即num[i]<=i
。
轮调:可以改变数组的起点,题目也有样例:数组为 nums = [2,4,1,3,0]
,我们按 k = 2 进行轮调后,它将变成 [1,3,0,2,4]
。这将记为 3 分,因为 1 > 0 [不计分]、3 > 1 [不计分]、0 <= 2 [计 1 分]、2 <= 3 [计 1 分],4 <= 4 [计 1 分]。
现在需要找到某个k使得分数最大,k不唯一的话取最小的k。
思路
暴力枚举k可不可行?反正我不会这么写。
要考虑一个元素是否得分,只需要考虑k取什么值的时候能令到这个数字得分。因为有轮调的存在,num[i]<=i
的i是可变的,但num[i]的值却是固定的,可以写成num[i]<=(i-k+n)%n
,k为轮调的值,n为数组元素个数。可以看到,要使得不等式成立,k就会有一个取值范围(题目给出的num中的值都小于它的长度,所以是一定有可行域的。)
首先我们要知道k的取值范围是[0,n-1],根据不等式,可以看到,k从0开始变大的话,(i-k+n)%n的值会变小,然后在变大,所以要以i与num[i]的大小关系分类讨论,如果i比num[i]小的话,num[i]能得分的k的取值范围只有[ i+1 , n+i-num[i] ]
一段,否则有[ 0 , i-num[i] ]
以及[ i+1 , n-1 ]
两段。(可以自己举例证明或推理证明)
我们可以在计算每个num[i]的有效的k的取值范围,然后再枚举k的每个值,看k固定时有多少个元素能够得分。答案取能得到最高分的k的最小值。
代码
class Solution {
public:
int bestRotation(vector<int>& nums) {
int n = nums.size();
vector<int> sum(n+1,0);
for(int i = 0;i < n; ++i){
int nk = nums[i];
if(i < nk){
++sum[1+i];
--sum[n+i-nk+1];
}else{
++sum[0];
--sum[i-nk+1];
++sum[1+i];
--sum[n];
}
}
int countNum = 0,ans = 0,maxNum=0;
for(int i = 0;i < n; ++i){
countNum += sum[i];
if(maxNum < countNum){
maxNum = countNum;
ans = i;
}
}
return ans;
}
};