leetcode 396 旋转函数 前缀和

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

# 396 旋转函数

### **[396. 旋转函数](https://leetcode-cn.com/problems/rotate-function/)**

难度中等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) = \quad \sum_{i = 0}^{n-1}(i*num_k[i])最大$

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

$=(n-1)*num_k[0]+(n-2)*num_k[1]+······+(n-n)*num_k[n]$

$=n*(\sum_{i=0}^{n-1}(num_k[i]))-\sum_{i=0}^{n-1}((i+1)*num_k[i])$

$=n*(\sum_{i=0}^{n-1}(num_k[i]))-\sum_{i=0}^{n-1}(\sum_{j=0}^{i}num_k[j])$

 $这里我们定义Sum(n) = \sum_{i=0}^{n-1}num_k[i]$

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

$接下来我们定义 presum_k(n) = \sum_{i=0}^{n-1}num_k[i] 为翻转k个数后的序列前缀和\quad其中有递推式 presum_k(n) = presum_k(n-1)+num_k[n]$也就是我们的前缀和

而这里由于需要我们也设定一个当旋转k后前缀和的总和函数 $Sum\_pre_k(n) = \sum_{i=0}^{n-1}presum(i+1)$

由此 我们再重新整理原式

$得 n*Sum(n)-Sum\_pre_k(n)$

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

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

因此 对于不同的k 仅有 $Sum\_pre_k(n)$在变换

所以原题就变成了求 $Sum\_pre_k$ 的最小值

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

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

那根据刚才的我们发现 实际 $Sum\_pre_k(n)$是前缀和的前缀和所以复杂度仅有O(n)

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

$Sum\_pre_k(n) = presum_k(1)+presum_k(2)+·····+presum_k(n)$

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

$Sum\_pre_{k+1}(n) = presum_{k+1}(1)+presum_{k+1}(2)+·····+presum_{k+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)$

 由此我们得到了递推公式 $Sum\_pre_{k+1}=Sum\_pre_{k}-n*num[0]+Sum(n)$

而最终解 $Min\_sum(n) = min(Min\_sum(n-1),Sum\_pre_{n})$

$f(k)_{min} = n*Sum(n)-Min\_sum(n)$

## 数据证明

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

$$
 nums = [4,3,2,6]
$$

$$
 Sum(4) = 4+3+2+6 = 15\\15*4 = 60
$$

$$
presum_0(4) = 4*4+3*3+2*2+6*1 = 35\\  60-35 = 25 \\Min\_sum(1) = 35
$$

$$
presum_1(4) = presum_0(4) -4*num[0] +Sum(4) = 35-4*4+15 = 34\\  60-34 = 26 \\Min\_sum(2) = 34
$$

$$
presum_2(4) = presum_1(4) -4*num[1] +Sum(4) = 34-4*3+15 = 37\\  60-37 = 23 \\Min\_sum(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
$$

$$
presum_4(4) = presum_3(4) -4*num[3] +Sum(4) = 44-4*6+15 = 35\\  60-35 = 25 \\Min\_sum(1) = 34
$$

$$
f(k)_{min} = 4*Sum(4)-Min\_sum(4) = 60 - 34 = 26
$$

# 数据规模分析

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

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

因此数据最大到 1e5*1e2*1e5/2 因此int可能会爆

# 算法实现

```cpp
    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、付费专栏及课程。

余额充值