LeetCode 798. 得分最高的最小轮调题解

798. 得分最高的最小轮调题解

题目来源:798. 得分最高的最小轮调

2022.03.09 每日一题

每日一题专栏地址:LeetCode 每日一题题解更新中❤️💕

我最初的想法是遍历数组,统计 n u m s [ i ] nums[i] nums[i] i i i 的差值,然后遍历 n − 1 n-1 n1次统计第一次出现最大值的 k

看了眼数据范围 1 0 5 10^5 105 这方法绝对TLE

然后看了三叶姐的题解传送门,斗胆来分析一波上下界分析+差分的方法

上下界分析+差分

首先对于数组 n u m s nums nums来说,他最多轮调 n − 1 n-1 n1次( 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 ik,但是可能出现 小于 0 的情况出现,这个时候, n u m s [ i ] nums[i] nums[i]就被放入了数组的后面 其索引为 ( i − k + n ) (i-k+n)%n (ik+n)

求得了索引的范围以后,我们知道 索引的范围应该在 $ [0,n)$,之中,我们可以通过这个来限制 k k k 的大小范围
{ i − k > = 0 i − k < = n − 1 \begin{cases} i-k>=0 \\ i-k<=n-1 \end{cases} {ik>=0ik<=n1
此外,还需判断什么情况下会得分
i − k > = n u m s [ i ] i-k>=nums[i] ik>=nums[i]
根据题目中的描述 $nums[i] $的值都是大于 0

由以上几条可以知道 k 最后的范围是
i + 1 − n < = k < = i − n u m s [ i ] i+1-n<=k<=i-nums[i] i+1n<=k<=inums[i]

但是,由于 i − ( n − 1 ) i-(n-1) i(n1)可能会出现负值的情况,因此我们需要对范围进行取余拆分

拆分为如下两段
{ [ 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,inums[i]][i(n1),n1]
最后分别求出数组中每个元素对应的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;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值