cpp
class Solution {
public:
int findContentChildren(vector<int>& g, vector<int>& s) {
sort(g.begin(), g.end());
sort(s.begin(), s.end());
int ans = 0, index = s.size()-1;
for(int i = g.size() -1; i >= 0; i--){
if(index >= 0 && s[index] >= g[i]){
ans++;
index--;
}
}
return ans;
}
};
cpp
class Solution {
public:
int wiggleMaxLength(vector<int>& nums) {
int ans = 1;
int pre = 0;// 维护差值
for(int i = 1; i < nums.size(); i++){
int dif = nums[i] - nums[i-1];
if(dif > 0 && pre <= 0){
ans++;
pre = 1;
} else if(dif < 0 && pre >= 0){
ans++;
pre= -1;
}
}
return ans;
}
};
cpp
class Solution {
public:
int maxSubArray(vector<int>& nums) {
int pre = 0, mn = 0, ans = INT_MIN;
for(auto x : nums){
pre += x; // 计算每个位置的前缀和
ans = max(ans, pre - mn);
mn = min(mn, pre);
}
return ans;
}
};
好的,我们把"前缀和解法"的这几行代码逐句吃透,讲清楚每个变量代表什么、不变量是什么、更新顺序为何这样,再走一个完整例子。
long long pref = 0, mn = 0, ans = LLONG_MIN;
for (int x : nums) {
pref += x; // 1) 累加到当前位置的前缀和 S[j]
ans = max(ans, pref - mn);// 2) 用 S[j] 减"历史最小前缀"得到当前最优子数组和
mn = min(mn, pref); // 3) 更新历史最小前缀,为下一步做准备
}
return (int)ans;
1) 三个变量各自的含义
-
pref
:到当前下标j
的前缀和 S[j]S[j]。 -
mn
:从开头到"上一位置"为止的历史最小前缀和 mini≤j−1S[i]\min_{i\le j-1} S[i]。初始化为 0,对应"空前缀"(让子数组可以从 0 开始)。 -
ans
:目前为止见到的最大子数组和。
关键恒等式:任意区间 (i,j](i, j] 的和 = S[j]−S[i]S[j] - S[i]。
所以固定
j
时,要让区间和最大,就让减去的 S[i]S[i] 尽量小 ⇒ 用"到目前为止最小的前缀和"。
2) 为何先算 ans
再更新 mn
顺序必须是:
-
先用旧的
mn
(只含直到j-1
的前缀)计算pref - mn
;这对应某个非空区间 (i,j](i, j]。 -
再把当前
pref
纳入mn
的候选,供下一个 j+1 使用。
若反过来先更新 mn=min(mn,pref)
,那 mn
可能等于 pref
,pref - mn
变 0,会把"空区间"当成候选,破坏正确性。
3) 为什么这样就对?(不变量 + 上界可达)
对每个位置 j
,这一轮循环后满足:
-
pref
恰是 S[j]S[j]; -
mn
成为 mini≤jS[i]\min_{i\le j} S[i](已纳入当前前缀); -
ans
等于 maxt≤j maxi<=t−1(S[t]−mink≤t−1S[k])\max_{t\le j}\ \max_{i< = t-1}(S[t]-\min_{k\le t-1}S[k]),也就是前j
个位置里"每个t
的最佳子数组"的最大值。递推到最后就是全局最优。
全负数组 也没问题:
第一个数 x<0
时,pref=x, ans=max(-inf, x-0)=x
;随后 mn=min(0,x)=x
,后续不会把空区间当解,最终会选到"绝对值最小的那个负数"。
4) 一个完整示例走表(LeetCode 经典)
nums = [-2, 1, -3, 4, -1, 2, 1, -5, 4]
,最优是 [4,-1,2,1]
,和为 6。
j | x | pref(=S[j]) | mn(用前) | pref-mn | ans(更后) | mn(更新后) |
---|---|---|---|---|---|---|
0 | -2 | -2 | 0 | -2 | -2 | -2 |
1 | 1 | -1 | -2 | 1 | 1 | -2 |
2 | -3 | -4 | -2 | -2 | 1 | -4 |
3 | 4 | 0 | -4 | 4 | 4 | -4 |
4 | -1 | -1 | -4 | 3 | 4 | -4 |
5 | 2 | 1 | -4 | 5 | 5 | -4 |
6 | 1 | 2 | -4 | 6 | 6 | -4 |
7 | -5 | -3 | -4 | 1 | 6 | -4 |
8 | 4 | 1 | -4 | 5 | 6 | -4 |
- 第 6 步得到
ans=6
,对应S[6]-mn = 2 - (-4)
,也就是区间(idx=2, 6] = [4,-1,2,1]
。
5) 和 Kadane 的关系
Kadane 通过"以 i 结尾 的最优"和"全局最优"递推;
前缀和法通过"到 j 的前缀 "与"历史最小前缀"做差。本质相同:
maxi<j(S[j]−S[i])≡max_j{S[j]−mini<jS[i]}.\max_{i<j}(S[j]-S[i]) \equiv \max\j\{S[j]-\min{i<j}S[i]\}.
6) 复杂度 & 小坑
-
时间
O(n)
,空间O(1)
。 -
用
long long
防止相加溢出;返回时转回int
(题目范围安全)。 -
这题是连续子数组(subarray),不是子序列(subsequence)。
如果你想把"具体子数组的左右下标"也返回,我可以在这版里再加上"记录达到 mn
的位置"和"刷新 ans
时的右端点",一眼就能定位区间。