leetcode 396 旋转数组前缀和

6 篇文章 0 订阅
2 篇文章 0 订阅

396 旋转函数

396. 旋转函数

难度中等192

给定一个长度为 n 的整数数组 nums 。

假设 arrk 是数组 nums 顺时针旋转 k 个位置后的数组,我们定义 nums 的 旋转函数  F 为:

  • F(k) = 0 * arrk[0] + 1 * arrk[1] + ... + (n - 1) * arrk[n - 1]

返回 F(0), F(1), ..., F(n-1)中的最大值 。

生成的测试用例让答案符合 32 位 整数。

示例 1:

输入: nums = [4,3,2,6]
输出: 26
解释:
F(0) = (0 * 4) + (1 * 3) + (2 * 2) + (3 * 6) = 0 + 3 + 4 + 18 = 25
F(1) = (0 * 6) + (1 * 4) + (2 * 3) + (3 * 2) = 0 + 4 + 6 + 6 = 16
F(2) = (0 * 2) + (1 * 6) + (2 * 4) + (3 * 3) = 0 + 6 + 8 + 9 = 23
F(3) = (0 * 3) + (1 * 2) + (2 * 6) + (3 * 4) = 0 + 2 + 12 + 12 = 26
所以 F(0), F(1), F(2), F(3) 中的最大值是 F(3) = 26 。

示例 2:

输入: nums = [100]
输出: 0

提示:

  • n == nums.length
  • 1 <= n <= 105
  • 100 <= nums[i] <= 100

通过次数34,611提交次数66,201

题目分析

由 题 意 , 我 们 这 里 要 求 k 使 得 f ( k ) = ∑ i = 0 n − 1 ( i ∗ n u m k [ i ] ) 最 大 由题意,我们这里要求k使得f(k) = \quad \sum_{i = 0}^{n-1}(i*num_k[i])最大 k使f(k)=i=0n1(inumk[i])

因 此 我 们 可 以 展 开 该 式 有 , 因此我们可以展开该式有,

= ( n − 1 ) ∗ n u m k [ 0 ] + ( n − 2 ) ∗ n u m k [ 1 ] + ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ + ( n − n ) ∗ n u m k [ n ] =(n-1)*num_k[0]+(n-2)*num_k[1]+······+(n-n)*num_k[n] =(n1)numk[0]+(n2)numk[1]++(nn)numk[n]

= n ∗ ( ∑ i = 0 n − 1 ( n u m k [ i ] ) ) − ∑ i = 0 n − 1 ( ( i + 1 ) ∗ n u m k [ i ] ) =n*(\sum_{i=0}^{n-1}(num_k[i]))-\sum_{i=0}^{n-1}((i+1)*num_k[i]) =n(i=0n1(numk[i]))i=0n1((i+1)numk[i])

= n ∗ ( ∑ i = 0 n − 1 ( n u m k [ i ] ) ) − ∑ i = 0 n − 1 ( ∑ j = 0 i n u m k [ j ] ) =n*(\sum_{i=0}^{n-1}(num_k[i]))-\sum_{i=0}^{n-1}(\sum_{j=0}^{i}num_k[j]) =n(i=0n1(numk[i]))i=0n1(j=0inumk[j])

这 里 我 们 定 义 S u m ( n ) = ∑ i = 0 n − 1 n u m k [ i ] 这里我们定义Sum(n) = \sum_{i=0}^{n-1}num_k[i] Sum(n)=i=0n1numk[i]

即为翻转k次所有项的和,其中由于无论如何旋转元素是不变的,因此等价于数组的总和

接 下 来 我 们 定 义 p r e s u m k ( n ) = ∑ i = 0 n − 1 n u m k [ i ] 为 翻 转 k 个 数 后 的 序 列 前 缀 和 其 中 有 递 推 式 p r e s u m k ( n ) = p r e s u m k ( n − 1 ) + n u m k [ n ] 接下来我们定义 presum_k(n) = \sum_{i=0}^{n-1}num_k[i] 为翻转k个数后的序列前缀和\quad其中有递推式 presum_k(n) = presum_k(n-1)+num_k[n] presumk(n)=i=0n1numk[i]kpresumk(n)=presumk(n1)+numk[n]也就是我们的前缀和

而这里由于需要我们也设定一个当旋转k后前缀和的总和函数 S u m _ p r e k ( n ) = ∑ i = 0 n − 1 p r e s u m ( i + 1 ) Sum\_pre_k(n) = \sum_{i=0}^{n-1}presum(i+1) Sum_prek(n)=i=0n1presum(i+1)

由此 我们再重新整理原式

得 n ∗ S u m ( n ) − S u m _ p r e k ( n ) 得 n*Sum(n)-Sum\_pre_k(n) nSum(n)Sum_prek(n)

因此这里我们原本的问题变成了求上式的最大值

由于我们之前分析过,无论如何旋转序列元素不变所以元素总和不变

因此 对于不同的k 仅有 S u m _ p r e k ( n ) Sum\_pre_k(n) Sum_prek(n)在变换

所以原题就变成了求 S u m _ p r e k Sum\_pre_k Sum_prek 的最小值

一般的法子则是 枚举所有情况求出所有的和 由于 可以反转 n次 长度也为n

所以复杂度为O(n^2) 这里 n < 1 e 5 n<1e5 n<1e5 显然会爆

那根据刚才的我们发现 实际 S u m _ p r e k ( n ) Sum\_pre_k(n) Sum_prek(n)是前缀和的前缀和所以复杂度仅有O(n)

但唯一问题是如何从旋转零次变换到旋转k次呢

S u m _ p r e k ( n ) = p r e s u m k ( 1 ) + p r e s u m k ( 2 ) + ⋅ ⋅ ⋅ ⋅ ⋅ + p r e s u m k ( n ) Sum\_pre_k(n) = presum_k(1)+presum_k(2)+·····+presum_k(n) Sum_prek(n)=presumk(1)+presumk(2)++presumk(n)

                       $=num_k[0] +num_k[0]+num_k[1]+·······$

S u m _ p r e k + 1 ( n ) = p r e s u m k + 1 ( 1 ) + p r e s u m k + 1 ( 2 ) + ⋅ ⋅ ⋅ ⋅ ⋅ + p r e s u m k + 1 ( n ) Sum\_pre_{k+1}(n) = presum_{k+1}(1)+presum_{k+1}(2)+·····+presum_{k+1}(n) Sum_prek+1(n)=presumk+1(1)+presumk+1(2)++presumk+1(n)

                       $=num_{k+1}[0] +num_{k+1}[0]+num_{k+1}[1]+·······$

                       $=num_{k}[1] +num_{k}[1]+num_{k}[2]+·······+num_{k}[0]+······num_k[n-1]$

                        $=Sum\_pre_{k}-n*num_k[0]+Sum(n)$

由此我们得到了递推公式 S u m _ p r e k + 1 = S u m _ p r e k − n ∗ n u m [ 0 ] + S u m ( n ) Sum\_pre_{k+1}=Sum\_pre_{k}-n*num[0]+Sum(n) Sum_prek+1=Sum_preknnum[0]+Sum(n)

而最终解 M i n _ s u m ( n ) = m i n ( M i n _ s u m ( n − 1 ) , S u m _ p r e n ) Min\_sum(n) = min(Min\_sum(n-1),Sum\_pre_{n}) Min_sum(n)=min(Min_sum(n1),Sum_pren)

f ( k ) m i n = n ∗ S u m ( n ) − M i n _ s u m ( n ) f(k)_{min} = n*Sum(n)-Min\_sum(n) f(k)min=nSum(n)Min_sum(n)

数据证明

最后抽象论证结束我们可以使用具体样例来进行检验

  n u m s = [ 4 , 3 , 2 , 6 ]  nums = [4,3,2,6]  nums=[4,3,2,6]

S u m ( 4 ) = 4 + 3 + 2 + 6 = 15 15 ∗ 4 = 60 Sum(4) = 4+3+2+6 = 15\\15*4 = 60 Sum(4)=4+3+2+6=15154=60

p r e s u m 0 ( 4 ) = 4 ∗ 4 + 3 ∗ 3 + 2 ∗ 2 + 6 ∗ 1 = 35 60 − 35 = 25 M i n _ s u m ( 1 ) = 35 presum_0(4) = 4*4+3*3+2*2+6*1 = 35\\ 60-35 = 25 \\Min\_sum(1) = 35 presum0(4)=44+33+22+61=356035=25Min_sum(1)=35

p r e s u m 1 ( 4 ) = p r e s u m 0 ( 4 ) − 4 ∗ n u m [ 0 ] + S u m ( 4 ) = 35 − 4 ∗ 4 + 15 = 34 60 − 34 = 26 M i n _ s u m ( 2 ) = 34 presum_1(4) = presum_0(4) -4*num[0] +Sum(4) = 35-4*4+15 = 34\\ 60-34 = 26 \\Min\_sum(2) = 34 presum1(4)=presum0(4)4num[0]+Sum(4)=3544+15=346034=26Min_sum(2)=34

p r e s u m 2 ( 4 ) = p r e s u m 1 ( 4 ) − 4 ∗ n u m [ 1 ] + S u m ( 4 ) = 34 − 4 ∗ 3 + 15 = 37 60 − 37 = 23 M i n _ s u m ( 1 ) = 34 presum_2(4) = presum_1(4) -4*num[1] +Sum(4) = 34-4*3+15 = 37\\ 60-37 = 23 \\Min\_sum(1) = 34 presum2(4)=presum1(4)4num[1]+Sum(4)=3443+15=376037=23Min_sum(1)=34

p r e s u m 3 ( 4 ) = p r e s u m 2 ( 4 ) − 4 ∗ n u m [ 2 ] + S u m ( 4 ) = 37 − 4 ∗ 2 + 15 = 44 60 − 44 = 16 M i n _ s u m ( 1 ) = 34 presum_3(4) = presum_2(4) -4*num[2] +Sum(4) = 37-4*2+15 = 44\\ 60-44 = 16 \\Min\_sum(1) = 34 presum3(4)=presum2(4)4num[2]+Sum(4)=3742+15=446044=16Min_sum(1)=34

p r e s u m 4 ( 4 ) = p r e s u m 3 ( 4 ) − 4 ∗ n u m [ 3 ] + S u m ( 4 ) = 44 − 4 ∗ 6 + 15 = 35 60 − 35 = 25 M i n _ s u m ( 1 ) = 34 presum_4(4) = presum_3(4) -4*num[3] +Sum(4) = 44-4*6+15 = 35\\ 60-35 = 25 \\Min\_sum(1) = 34 presum4(4)=presum3(4)4num[3]+Sum(4)=4446+15=356035=25Min_sum(1)=34

f ( k ) m i n = 4 ∗ S u m ( 4 ) − M i n _ s u m ( 4 ) = 60 − 34 = 26 f(k)_{min} = 4*Sum(4)-Min\_sum(4) = 60 - 34 = 26 f(k)min=4Sum(4)Min_sum(4)=6034=26

数据规模分析

数组最大长度 1e5 数据最大1e2

根据公式 presum +=((size-i)*nums[i]);

因此数据最大到 1e51e21e5/2 因此int可能会爆

算法实现

    int maxRotateFunction(vector<int>& nums) {
        long long sum = 0;
        long long presum = 0;
        long long min_sum = 0;
        long long size = nums.size();
        for(long long i = 0;i<size;i++){
            sum+=nums[i];
            presum +=((size-i)*nums[i]);
        }
        min_sum = presum;
        for(int i = 0;i<size;i++){
            presum-=(size*nums[i]);
            presum+=sum;
            min_sum = min(min_sum,presum);
        }
        return sum*size-min_sum;
    }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值