给定一个数组 A,我们可以将它按一个非负整数 K 进行轮调,这样可以使数组变为 A[K], A[K+1], A{K+2], … A[A.length - 1], A[0], A[1], …, A[K-1] 的形式。此后,任何值小于或等于其索引的项都可以记作一分。
例如,如果数组为 [2, 4, 1, 3, 0],我们按 K = 2 进行轮调后,它将变成 [1, 3, 0, 2, 4]。这将记作 3 分,因为 1 > 0 [no points], 3 > 1 [no points], 0 <= 2 [one point], 2 <= 3 [one point], 4 <= 4 [one point]。
在所有可能的轮调中,返回我们所能得到的最高分数对应的轮调索引 K。如果有多个答案,返回满足条件的最小的索引 K。
示例 1:
输入:[2, 3, 1, 4, 0]
输出:3
解释:
下面列出了每个 K 的得分:
K = 0, A = [2,3,1,4,0], score 2
K = 1, A = [3,1,4,0,2], score 3
K = 2, A = [1,4,0,2,3], score 3
K = 3, A = [4,0,2,3,1], score 4
K = 4, A = [0,2,3,1,4], score 3
所以我们应当选择 K = 3,得分最高。
示例 2:
输入:[1, 3, 0, 2, 4]
输出:0
解释:
A 无论怎么变化总是有 3 分。
所以我们将选择最小的 K,即 0。
提示:
A 的长度最大为 20000。
A[i] 的取值范围是 [0, A.length]。
思路分析:\color{blue}思路分析:思路分析:这道题可能大家都是想着直接移动,然后计算分数,再取最高值。蛋式A的长度比较大的时候耗时就非常大了。
我们以A=[2,3,1,4,0]为例寻找规律:
A[0]=2移动到 2 号索引位置[4,0,2,3,1]其对应的K为3=(0-A[0]+5)%5
A[1]=3移动到 3 号索引位置[0,2,3,1,4]其对应的K为3=(1-A[1]+5)%5
A[2]=1移动到 1 号索引位置[3,1,4,0,2]其对应的K为1=(2-A[2]+5)%5
A[3]=4移动到 4 号索引位置[0,2,3,1,4]其对应的K为1=(3-A[3]+5)%5
A[4]=0移动到 0 号索引位置[0,2,3,1,4]其对应的K为3=(4-A[4]+5)%5
由此可以得出一个公式,将A[i]向左移动到下标A[A[i]]的位置需要K = (i - A[i] + N) % N
并且我们发现,A[A[i]]是第一个A[i]能得分的位置,如果这时减小K,则A[i]继续得分,如果增大K则A[i]将不得分。
如果我们能够刚好把所有A[i]都移动到A[A[i]]的位置,那么我们拿到的分数肯定的是最高的,蛋式这种情况几乎不可能。
当我们把A[i]移动到A[A[i]]后,再向左移动一个位置(即K增加1)。A[i]的移动公式为K’ = (1 + i - A[i] + N) % N
这个时候A[i]刚好不得分。
我们可以在这个刚好不得分的k标记一下,通过+1进行标记,这个k就是 (i - A[i] + 1 + N) % N。用一个长度为N的myK数组,对于每个元素A[i],我们都找到其刚好不得分的k = (i - A[i] + 1 + N) % N,那么此时myK[k]就表示数组中的数字在K = k时,A数组中不得分的元素个数。
可以发现,如果当K = k时,A[i]刚好不得分,当K = k + 1时(左移一个)A[i]继续不得分,蛋式当K = k + 1时有一个元素开始得分了,就是在当K = k处于A[0]的元素开始得分!!!
因此递推公式为:myK[k + 1] += myK[k] - 1
class Solution {
public:
int bestRotation(vector<int>& A) {
int n = A.size(), resK = 0;//resK用于记录当K = resK时,此时A数组中部分的个数最少(即A数组中得分的个数最多)
vector<int> myK(n, 0);//myK[k]代表当K = k时,A数组中不得分的个数
//第一步:将A数组中所有元素都向左移动(i - A[i] + 1 + n) % n个位置,即K = (i - A[i] + 1 + n) % n,此时A[i]刚好不得分
for (int i = 0; i < n; ++i) {
myK[(i - A[i] + 1 + n) % n] += 1;//当K = (i - A[i] + 1 + n) % n时,不得分的个数自增
}
//第二步:寻找最优的resK(当K = resK时,此时A数组中部分的个数最少(即A数组中得分的个数最多))
for (int i = 1; i < n; ++i) {
//递推式当K = i - 1增大到到K = i时
//在K = i - 1时不得分的继续不得分,蛋式当K = i - 1转换到K = i时,处于A[0]的元素开始得分
myK[i] += myK[i - 1] - 1;
if (myK[i] < myK[resK]){//K = resK时,此时A数组中部分的个数最少(即A数组中得分的个数最多)
resK = i;
}
}
return resK;
}
};