题目链接
1.题意的转换
题目要求从一排红包中找到两次切割点,使得三部分的总金额满足:
- 第一部分总金额 = 第三部分总金额。
我们可以把问题简化为寻找两个切割点(l 和 r),使得满足以下条件:
- 第一部分红包金额 = 从红包序列开头到切割点
l的金额之和。 - 第三部分红包金额 = 从切割点
r到红包序列末尾的金额之和。 - 上述两部分金额相等时,我们更新答案为当前的第一部分总金额,并继续寻找更大的可能值。
为有效实现这一目标,我们需要借助 前缀和 和 双指针 算法。
2. 算法思想讲解
1. 前缀和
前缀和是一种常见的算法技巧,用于快速计算数组中任意区间的元素之和。具体来说:
- 设
sum[i]表示数组前i个元素的累加和,即:sum[i]=redpacks[0]+redpacks[1]+⋯+redpacks[i−1]sum[i]=redpacks[0]+redpacks[1]+⋯+redpacks[i−1] - 有了前缀和,任何区间
[a, b]的和都可以在 O(1) 时间内通过公式计算:区间和=sum[b]−sum[a]区间和=sum[b]−sum[a]
在本题中:
- 我们通过前缀和快速计算从头到切割点
l的金额(第一部分的金额)。 - 同样,可以计算从切割点
r到数组末尾的金额(第三部分的金额)。
2. 双指针
双指针是一种常用于数组问题的优化算法,旨在通过两个指针移动来缩小搜索空间。本题中,双指针应用如下:
- 一个指针
l用于标记第一部分的结束位置(切割点)。 - 另一个指针
r用于标记第三部分的起始位置(第二个切割点)。 - 我们同时移动
l和r,以找到满足条件的切割点组合。
通过双指针,我们可以在 O(n) 的时间复杂度内找到最优解,而不需要暴力枚举所有可能的切割点组合。
3. 解题步骤
具体解题步骤
1. 计算前缀和
使用一个数组 sum 存储红包序列的前缀和:
- 初始化
sum[0] = 0。 - 遍历数组,依次计算
sum[i] = sum[i-1] + redpacks[i-1]。
2. 初始化双指针
l从 1 开始,因为第一部分至少需要有一个红包。r从数组末尾开始,保证第三部分至少有一个红包。
3. 双指针遍历寻找最大值
- 使用循环,当
l < r时,计算当前的两部分金额:suml=sum[l],sumr=sum[n]−sum[r−1]suml=sum[l],sumr=sum[n]−sum[r−1] - 如果
suml == sumr,更新答案,并移动指针l,尝试找到更大的可能值。 - 如果
suml < sumr,说明第一部分金额较小,移动l向右扩展。 - 如果
suml > sumr,说明第三部分金额较小,移动r向左收缩。
4. 返回结果
当循环结束时,结果 res 即为小U可以获得的最大奖金金额。
4 . 具体代码
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int solution(vector<int> redpacks) {
int n = redpacks.size();
int res = 0;
vector<int> sum(n + 1, 0);
// 前缀和计算
for (int i = 1; i <= n; ++i) {
sum[i] = sum[i - 1] + redpacks[i - 1];
}
int l = 1, r = n; // 双指针初始值
while (l < r) { // 遍历所有可能的切割点
int suml = sum[l]; // 左部分前缀和
int sumr = sum[n] - sum[r - 1]; // 右部分和
if (suml == sumr) {
res = max(res, suml);
l++; // 或移动一个指针
} else if (suml < sumr) {
l++;
} else {
r--;
}
}
return res;
}
算法复杂度分析
-
时间复杂度
- 前缀和计算:O(n)。
- 双指针遍历:每个指针最多移动 nn 次,总计 O(n)O(n)。
- 总体复杂度为 O(n)。
-
空间复杂度
- 仅需一个长度为 n+1n+1 的前缀和数组
sum,空间复杂度为 O(n)。
- 仅需一个长度为 n+1n+1 的前缀和数组