798. 得分最高的最小轮调题解
题目来源:798. 得分最高的最小轮调
2022.03.09 每日一题
每日一题专栏地址:LeetCode 每日一题题解更新中❤️💕
我最初的想法是遍历数组,统计 n u m s [ i ] nums[i] nums[i] 与 i i i 的差值,然后遍历 n − 1 n-1 n−1次统计第一次出现最大值的 k
看了眼数据范围
1
0
5
10^5
105 这方法绝对TLE
呀
然后看了三叶姐的题解传送门,斗胆来分析一波上下界分析+差分的方法
上下界分析+差分
首先对于数组 n u m s nums nums来说,他最多轮调 n − 1 n-1 n−1次( n n n 是数组的长度)
其次一个数字
n
u
m
s
[
i
]
nums[i]
nums[i],他能选择的下标是有$ n
个
,
其
范
围
是
个,其范围是
个,其范围是 [0,n)$,我们可以通过计算来求出 轮调
k
k
k 轮后,
n
u
m
s
[
i
]
nums[i]
nums[i] 所在的位置 是
i
−
k
i-k
i−k,但是可能出现 小于 0
的情况出现,这个时候,
n
u
m
s
[
i
]
nums[i]
nums[i]就被放入了数组的后面 其索引为
(
i
−
k
+
n
)
(i-k+n)%n
(i−k+n),
求得了索引的范围以后,我们知道 索引的范围应该在 $ [0,n)$,之中,我们可以通过这个来限制
k
k
k 的大小范围
{
i
−
k
>
=
0
i
−
k
<
=
n
−
1
\begin{cases} i-k>=0 \\ i-k<=n-1 \end{cases}
{i−k>=0i−k<=n−1
此外,还需判断什么情况下会得分
i
−
k
>
=
n
u
m
s
[
i
]
i-k>=nums[i]
i−k>=nums[i]
根据题目中的描述 $nums[i] $的值都是大于 0
的
由以上几条可以知道 k 最后的范围是
i
+
1
−
n
<
=
k
<
=
i
−
n
u
m
s
[
i
]
i+1-n<=k<=i-nums[i]
i+1−n<=k<=i−nums[i]
但是,由于 i − ( n − 1 ) i-(n-1) i−(n−1)可能会出现负值的情况,因此我们需要对范围进行取余拆分
拆分为如下两段
{
[
0
,
i
−
n
u
m
s
[
i
]
]
[
i
−
(
n
−
1
)
,
n
−
1
]
\begin{cases} [0,i-nums[i]]\\ [i-(n-1),n-1] \end{cases}
{[0,i−nums[i]][i−(n−1),n−1]
最后分别求出数组中每个元素对应的k
的范围,取出其中的最大值即可
class Solution {
public:
int bestRotation(vector<int> nums) {
// 获取数组长度
int len = nums.size();
// 定义数组来统计 k 的范围
vector<int> count(100001);
// 遍历数组,寻找各个元素中 k 的范围
for (int i = 0; i < len; i++) {
// 为了防止越界问题的发生,加上数组的长度并对其取余,来避免该问题
int t1 = (i - (len - 1) + len) % len, t2 = (i - nums[i] + len) % len;
// 采用 count[a]++; count[b+1]--;的原因是因为,
// 希望在 a 身后的元素都会有 +1 的举动,
// 而 b-- 是为了不让 范围截至的地方 b 以后的元素受到影响
// 如果 t1<=t2 则说明满足i+1-n<=i-nums[i] 的要求
if (t1 <= t2) {
count[t1]++;
count[t2 + 1]--;
} else {
// 反之则不满足,需要采用下方的两个要求
count[0]++;
count[t2 + 1]--;
count[t1]++;
count[len - 1 + 1]--;
}
}
// 加和统计总个数
for (int i = 1; i < len; i++) count[i] += count[i - 1];
// 一次寻找最大值
int res = 0;
for (int i = 1; i < len; i++) {
if (count[i] > count[res]) res = i;
}
return res;
}
};
class Solution {
public int bestRotation(int[] nums) {
// 获取数组长度
int len = nums.length;
// 定义数组来统计 k 的范围
int[] count = new int[100001];
// 遍历数组,寻找各个元素中 k 的范围
for (int i = 0; i < len; i++) {
// 为了防止越界问题的发生,加上数组的长度并对其取余,来避免该问题
int t1 = (i - (len - 1) + len) % len, t2 = (i - nums[i] + len) % len;
// 采用 count[a]++; count[b+1]--;的原因是因为,
// 希望在 a 身后的元素都会有 +1 的举动,
// 而 b-- 是为了不让 范围截至的地方 b 以后的元素受到影响
// 如果 t1<=t2 则说明满足i+1-n<=i-nums[i] 的要求
if (t1 <= t2) {
count[t1]++;
count[t2 + 1]--;
} else {
// 反之则不满足,需要采用下方的两个要求
count[0]++;
count[t2 + 1]--;
count[t1]++;
count[len - 1 + 1]--;
}
}
// 加和统计总个数
for (int i = 1; i < len; i++) count[i] += count[i - 1];
// 一次寻找最大值
int res = 0;
for (int i = 1; i < len; i++) {
if (count[i] > count[res]) res = i;
}
return res;
}
}