题目描述
思路解析动画文字版
把这句话记死:「整体加一个 sum,再减掉 n 乘以转到队首的那个元素」。下面每一帧都在套它。
递推需要一个起点,所以第一步先按定义老实算出 F(0),顺便把所有元素之和 sum 也累出来。下面从下标 0 开始,一个元素一个元素地往里加。
看下标 0 的元素 4(紫色)。它对 F(0) 的贡献是下标乘元素,也就是 0 × 4 = 0,把它加进 F(0);同时把 4 也累进 sum。左边蓝色是已经算过的,灰色是还没轮到的。
看下标 1 的元素 3(紫色)。它对 F(0) 的贡献是下标乘元素,也就是 1 × 3 = 3,把它加进 F(0);同时把 3 也累进 sum。左边蓝色是已经算过的,灰色是还没轮到的。
看下标 2 的元素 2(紫色)。它对 F(0) 的贡献是下标乘元素,也就是 2 × 2 = 4,把它加进 F(0);同时把 2 也累进 sum。左边蓝色是已经算过的,灰色是还没轮到的。
看下标 3 的元素 6(紫色)。它对 F(0) 的贡献是下标乘元素,也就是 3 × 6 = 18,把它加进 F(0);同时把 6 也累进 sum。左边蓝色是已经算过的,灰色是还没轮到的。
看下标 4 的元素 4(紫色)。它对 F(0) 的贡献是下标乘元素,也就是 4 × 4 = 16,把它加进 F(0);同时把 4 也累进 sum。左边蓝色是已经算过的,灰色是还没轮到的。
五个元素都加完:F(0) = 41,sum = 19。F(0) 既是第一个候选答案,也是后面递推的起点,所以当前最大 ans 先记成 41。接下来不再重新加权求和,全靠递推一格一格滚。
先看清旋转一格发生了什么:排在最后、系数 4 的红色元素 nums[4]=4 会转到最前面变成系数 0;其余每个元素系数都加 1。系数集体加 1 = 整体加 sum,红色元素从 4× 变 0× = 减掉 n×它。这就是递推式的来历。
第 1 次旋转:这次转到队首的是下标 4 的红色元素 4。套递推式,先把数字代进去:上一帧的 F(0)=41,加上 sum=19,再减掉 n × nums[4] = 5 × 4。
把它算干净:41 + 19 先得 60,再减 5 × 4 = 20,于是 F(1) = 40。注意这一步只做了一次加法和一次减法,O(1) 就推出来了,完全没有重新加权求和。
拿 F(1)=40 和当前最大 41 比:没超过,最大答案保持 41 不动,继续往后滚。
第 2 次旋转:这次转到队首的是下标 3 的红色元素 6。套递推式,先把数字代进去:上一帧的 F(1)=40,加上 sum=19,再减掉 n × nums[3] = 5 × 6。
把它算干净:40 + 19 先得 59,再减 5 × 6 = 30,于是 F(2) = 29。注意这一步只做了一次加法和一次减法,O(1) 就推出来了,完全没有重新加权求和。
拿 F(2)=29 和当前最大 41 比:没超过,最大答案保持 41 不动,继续往后滚。
第 3 次旋转:这次转到队首的是下标 2 的红色元素 2。套递推式,先把数字代进去:上一帧的 F(2)=29,加上 sum=19,再减掉 n × nums[2] = 5 × 2。
把它算干净:29 + 19 先得 48,再减 5 × 2 = 10,于是 F(3) = 38。注意这一步只做了一次加法和一次减法,O(1) 就推出来了,完全没有重新加权求和。
拿 F(3)=38 和当前最大 41 比:没超过,最大答案保持 41 不动,继续往后滚。
第 4 次旋转:这次转到队首的是下标 1 的红色元素 3。套递推式,先把数字代进去:上一帧的 F(3)=38,加上 sum=19,再减掉 n × nums[1] = 5 × 3。
把它算干净:38 + 19 先得 57,再减 5 × 3 = 15,于是 F(4) = 42。注意这一步只做了一次加法和一次减法,O(1) 就推出来了,完全没有重新加权求和。
拿 F(4)=42 和当前最大 41 比:42 更大,刷新最大答案,ans 变成 42(整排标绿表示出现了新的最大)。
全部 5 个 F 值滚完:F(0) 到 F(4) 分别是 41、40、29、38、42,其中最大的是 F(4) = 42,这就是答案。回头看,我们只在开头老实算了一次 F(0),之后每个 F(k) 都是 O(1) 推出来的,整体 O(n)。
边界先想清:单元素答案恒为 0;长度为 1 时递推循环根本不进;负数也照样按公式滚,不用特判。
认出「相邻状态作差」这个母题,一大类把暴力压成线性的题都能套。
参考代码
from __future__ import annotationsfrom typing import *from collections import *from functools import *from itertools import *from math import *from heapq import *from bisect import *class Solution: def maxRotateFunction(self, nums: List[int]) -> int: f = sum(i * v for i, v in enumerate(nums)) n, s = len(nums), sum(nums) ans = f for i in range(1, n): f = f + s - n * nums[n - i] ans = max(ans, f) return ans复杂度
- 时间:O(n),算 F(0) 一遍,递推再一遍
- 空间:O(1),只用 f、s、ans 几个变量
易错点
面试追问把动画讲成自己的话
追问怎么想到这条递推的?
追问答案会不会溢出?
这道题到这就讲完了。动画和文字是同一套思路——别光看,关掉页面自己默写一遍。然后顺着主线继续:
等差数列划分
LeetCode 413 · 中等 · 沿着 动态规划套路 继续往下推进
把这道题真正学会,再走
图解算法年卡 ¥99 /年
- ✓本题每步动画的吴师兄语音讲解(全站陆续覆盖)
- ✓小欧带 8 步通关训练——追问到不看答案也能写对、能 30 秒讲给面试官听
- ✓学习报告,记录每道题的掌握程度
76k+ GitHub Star · 吴师兄开源图解算法,几十万开发者在看的算法讲解
想成体系刷透这类套路?去图解算法专题