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=0n−1(i∗numk[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] =(n−1)∗numk[0]+(n−2)∗numk[1]+⋅⋅⋅⋅⋅⋅+(n−n)∗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=0n−1(numk[i]))−∑i=0n−1((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=0n−1(numk[i]))−∑i=0n−1(∑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=0n−1numk[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=0n−1numk[i]为翻转k个数后的序列前缀和其中有递推式presumk(n)=presumk(n−1)+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=0n−1presum(i+1)
由此 我们再重新整理原式
得 n ∗ S u m ( n ) − S u m _ p r e k ( n ) 得 n*Sum(n)-Sum\_pre_k(n) 得n∗Sum(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_prek−n∗num[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(n−1),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=n∗Sum(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=1515∗4=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)=4∗4+3∗3+2∗2+6∗1=3560−35=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)−4∗num[0]+Sum(4)=35−4∗4+15=3460−34=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)−4∗num[1]+Sum(4)=34−4∗3+15=3760−37=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)−4∗num[2]+Sum(4)=37−4∗2+15=4460−44=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)−4∗num[3]+Sum(4)=44−4∗6+15=3560−35=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=4∗Sum(4)−Min_sum(4)=60−34=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;
}