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 时的右端点",一眼就能定位区间。