"困难",灵茶山艾府解法,b( ̄▽ ̄)d:
cpp
class Solution {
public:
long long maxRunTime(int n, vector<int>& batteries) {
// reduce累加函数,用于计算所有电池的总电量
// 总电量决定了 "同时运行 n 台电脑" 的理论上限(最多能运行 tot / n
// 分钟,因为 n 台电脑同时运行时,每分钟消耗 n 单位电量)。
long long tot = reduce(batteries.begin(), batteries.end(), 0LL);
// 二分查找的初始化
long long l = 0, r = tot / n + 1;
//l(左边界):最短可能时间(0 分钟)。
//r(右边界):最长可能时间(总电量除以n,加 1 是为了覆盖边界情况)。
while (l + 1 < r) {
long long x = l + (r - l) / 2; // 中间值(当前尝试的运行时间)
long long sum = 0;
for (long long b : batteries) {
sum += min(b,x); // 每个电池最多贡献x分钟(因为运行x分钟的话,电池用不完也只能贡献x)
}
// 判断:如果总贡献>=n*x(n台电脑运行x分钟需要的总电量),说明x可行
(n * x <= sum ? l : r) = x;
// 语法结构:(条件 ? 变量A : 变量B) = 值;
// 等价于:如果条件成立,就把「值」赋给变量A;否则,把「值」赋给变量 B
// x 可行就把左边界右移(尝试更长时间),x 不可行就把右边界左移(尝试更短时间)
}
//二分结束后,l 就是最大的可行时间(因为循环条件是l+1 < r,最终 l 会停在最大的可行值)
return l;
}
};
示例:
n=2, batteries=[3,3,3],总电量 tot=9,初始 l=0, r=5。
第一次循环:x = (0+5)/2 = 2
- 计算 sum:每个电池最多贡献 2,sum=2+2+2=6;
- 判断条件:nx=22=4 ≤ 6 → 条件成立;
- 执行
l = x→ l 更新为 2(现在 l=2,r=5,后续尝试更长时间)。
第二次循环:x = (2+5)/2 = 3
- sum=3+3+3=9;
- nx=23=6 ≤9 → 条件成立;
- 执行
l = x→ l 更新为 3(l=3,r=5)。
第三次循环:x = (3+5)/2 =4
- sum=3+3+3=9;
- nx=24=8 ≤9 → 条件成立;
- 执行
l = x→ l 更新为 4(l=4,r=5)。
第四次循环:x = (4+5)/2 =4(整数除法)
- sum=9;
- nx=24=8 ≤9 → 条件成立;
- 执行
l = x→ l 还是 4(此时 l+1=5 = r,循环结束)。
最终返回 l=4,就是最大可行时间 ------ 和之前的结果一致。
reduce :
C++17 引入的标准库函数 (定义在 <numeric> 头文件中),核心作用是对序列中的元素执行「归约操作」(如累加、累乘) ,并支持并行计算,是处理大规模数据的高效工具。
一、核心功能
将一个序列(如数组、vector)的元素,通过「二元操作」合并为一个值(默认是加法,也可自定义操作)。例如:
- 对
[1,2,3,4]归约(默认加法)→ 结果是1+2+3+4=10; - 对
[2,3,4]归约(自定义乘法)→ 结果是2×3×4=24。
二、基本语法(常用重载形式)
1. 无初始值,默认加法
cpp
#include <numeric>
#include <vector>
std::vector<int> nums = {1,2,3,4};
// 结果 = 1+2+3+4 = 10
int sum = std::reduce(nums.begin(), nums.end());
- 说明:默认初始值是「元素类型的默认值」(如
int是0,float是0.0)。
2. 带初始值,默认加法
cpp
std::vector<int> nums = {1,2,3,4};
// 初始值=5,结果=5+1+2+3+4=15
int sum = std::reduce(nums.begin(), nums.end(), 5);
3. 带初始值 + 自定义操作
cpp
#include <functional> // 需包含此头文件以使用 std::multiplies
std::vector<int> nums = {2,3,4};
// 初始值=1,操作=乘法 → 结果=1×2×3×4=24
int product = std::reduce(nums.begin(), nums.end(), 1, std::multiplies<>());
- 常用操作(来自
<functional>):std::plus<>:加法(默认);std::multiplies<>:乘法;std::bit_or<>:按位或;- 也可以用Lambda 表达式自定义操作。
4. 并行执行(提升大规模数据效率)
cpp
#include <execution> // 需包含此头文件以使用并行策略
std::vector<int> big_nums(1000000, 1); // 100万个1
// 并行累加,速度比单线程快
int total = std::reduce(std::execution::par, big_nums.begin(), big_nums.end());
三、和 accumulate 的区别(关键!)
| 特性 | std::reduce(C++17) |
std::accumulate(C++98) |
|---|---|---|
| 执行顺序 | 无序(可并行) | 严格从左到右(串行) |
| 并行支持 | 支持(通过 std::execution::par) |
不支持 |
| 操作要求 | 需满足「结合律 + 交换律」(如加法、乘法) | 仅需结合律(如字符串拼接) |
| 初始值 | 可选(默认用类型默认值) | 必须提供 |
四、注意事项
-
操作的结合律 / 交换律 :并行模式下,
reduce会打乱元素顺序执行操作,因此操作必须满足「结合律 + 交换律」(如加法、乘法)。如果用减法、除法(不满足交换律),结果会不确定。 -
初始值类型 :若序列是
float/long long,初始值要对应类型(如0LL对应long long),避免类型不匹配。 -
头文件依赖:
- 基础用法:需包含
<numeric>; - 自定义操作:需包含
<functional>; - 并行执行:需包含
<execution>。
- 基础用法:需包含
五、示例代码(综合用法)
cpp
#include <iostream>
#include <vector>
#include <numeric>
#include <functional>
#include <execution>
int main() {
// 1. 基本累加
std::vector<int> nums = {1,2,3,4,5};
int sum = std::reduce(nums.begin(), nums.end());
std::cout << "Sum: " << sum << "\n"; // 输出 15
// 2. 自定义乘法
int product = std::reduce(nums.begin(), nums.end(), 1, std::multiplies<>());
std::cout << "Product: " << product << "\n"; // 输出 120
// 3. Lambda自定义操作(计算平方和)
int sum_sq = std::reduce(nums.begin(), nums.end(), 0,
[](int a, int b) { return a + b*b; });
std::cout << "Sum of squares: " << sum_sq << "\n"; // 输出 1+4+9+16+25=55
// 4. 并行计算
std::vector<int> big_data(1000000, 2);
int total = std::reduce(std::execution::par, big_data.begin(), big_data.end());
std::cout << "Total (parallel): " << total << "\n"; // 输出 2000000
return 0;
}
总结
reduce 是 高效的归约工具 ,适合处理大规模数据(尤其是并行场景);如果需要严格的执行顺序(如字符串拼接、减法),则用 accumulate。
std::reduce 最常用场景的代码模板:
「累加、累乘、自定义操作、并行计算」核心场景:
模板说明
- 所有模板需包含基础头文件:
#include <numeric>(reduce核心头文件); - 模板中
std::vector可替换为其他序列容器(如std::array、std::list),只需修改容器类型和初始化方式; - 支持 C++17 及以上版本(若编译器不支持,可将
std::reduce替换为std::accumulate,大部分场景语法兼容)。
一、基础场景:数值累加(最常用)
场景 1:无初始值(默认从 0 开始累加)
cpp
#include <iostream>
#include <numeric>
#include <vector>
int main() {
// 待累加的序列(可替换为任意数值类型:int、long long、float 等)
std::vector<int> nums = {1, 3, 5, 7, 9};
// 调用 reduce 累加(默认加法,初始值为 int 类型默认值 0)
int sum = std::reduce(nums.begin(), nums.end());
// 输出结果:1+3+5+7+9 = 25
std::cout << "累加结果:" << sum << std::endl;
return 0;
}
场景 2:带初始值的累加(灵活控制起始值)
cpp
#include <iostream>
#include <numeric>
#include <vector>
int main() {
std::vector<long long> batteries = {3, 3, 3}; // 电池容量序列(用 long long 避免溢出)
// 带初始值 0LL(LL 表示 long long 类型,匹配容器元素类型)
long long total = std::reduce(batteries.begin(), batteries.end(), 0LL);
// 输出结果:0+3+3+3 = 9(对应之前 Kafka 题目中的总电量计算)
std::cout << "电池总电量:" << total << std::endl;
return 0;
}
二、常用扩展:自定义二元操作
场景 3:累乘(计算序列乘积)
cpp
#include <iostream>
#include <numeric>
#include <vector>
#include <functional> // 需包含此头文件,使用 std::multiplies
int main() {
std::vector<int> nums = {2, 3, 4, 5};
// 初始值 1(乘法的单位元,乘 1 不改变结果),操作:std::multiplies<>()(乘法)
int product = std::reduce(nums.begin(), nums.end(), 1, std::multiplies<>());
// 输出结果:1×2×3×4×5 = 120
std::cout << "累乘结果:" << product << std::endl;
return 0;
}
场景 4:Lambda 自定义操作(平方和、最大值、字符串拼接等)
子场景 4.1:计算平方和
cpp
#include <iostream>
#include <numeric>
#include <vector>
int main() {
std::vector<int> nums = {1, 2, 3, 4};
// Lambda 表达式:a 是累加结果,b 是当前元素,返回 a + b²
int sum_sq = std::reduce(nums.begin(), nums.end(), 0,
[](int a, int b) { return a + b * b; }
);
// 输出结果:0 + 1² + 2² + 3² + 4² = 30
std::cout << "平方和结果:" << sum_sq << std::endl;
return 0;
}
子场景 4.2:查找序列最大值(自定义比较逻辑)
cpp
#include <iostream>
#include <numeric>
#include <vector>
int main() {
std::vector<int> scores = {85, 92, 78, 95, 88};
// Lambda 表达式:a 是当前最大值,b 是当前元素,返回两者较大值
int max_score = std::reduce(scores.begin(), scores.end(), scores[0],
[](int a, int b) { return a > b ? a : b; }
);
// 输出结果:95
std::cout << "最高分数:" << max_score << std::endl;
return 0;
}
子场景 4.3:字符串拼接(注意:并行模式不支持!)
cpp
#include <iostream>
#include <numeric>
#include <vector>
#include <string>
int main() {
std::vector<std::string> words = {"Hello", " ", "Spark", " ", "Kafka!"};
// Lambda 表达式:拼接两个字符串(初始值为空字符串)
std::string result = std::reduce(words.begin(), words.end(), std::string(""),
[](const std::string& a, const std::string& b) { return a + b; }
);
// 输出结果:Hello Spark Kafka!
std::cout << "拼接结果:" << result << std::endl;
return 0;
}
三、高性能场景:并行计算(大规模数据)
场景 5:并行累加(千万 / 亿级数据提速)
cpp
#include <iostream>
#include <numeric>
#include <vector>
#include <execution> // 需包含此头文件,使用并行策略
int main() {
// 生成 1000 万个 1(模拟大规模数据)
std::vector<int> big_data(10000000, 1);
// 并行策略:std::execution::par(多线程并行计算)
int total = std::reduce(std::execution::par, big_data.begin(), big_data.end());
// 输出结果:10000000(并行计算速度比单线程快 2~8 倍,取决于 CPU 核心数)
std::cout << "并行累加结果:" << total << std::endl;
return 0;
}
并行计算注意事项
- 必须包含头文件
<execution>; - 自定义操作必须满足「结合律 + 交换律」(如加法、乘法),否则结果不确定(例如减法、字符串拼接不能用并行);
- 适合处理「无状态、独立元素」的计算(如数值累加、累乘),大规模数据下优势明显。
四、兼容模板(C++17 以下版本,用 accumulate 替代)
若编译器不支持 C++17(reduce 是 C++17 新增),可直接替换为 accumulate,语法几乎一致:
cpp
#include <iostream>
#include <numeric>
#include <vector>
int main() {
std::vector<long long> batteries = {3, 3, 3};
// 把 reduce 换成 accumulate,参数完全不变
long long total = std::accumulate(batteries.begin(), batteries.end(), 0LL);
std::cout << "电池总电量:" << total << std::endl;
return 0;
}
总结
| 场景 | 核心模板代码片段 |
|---|---|
| 数值累加(带初始值) | std::reduce(v.begin(), v.end(), 0LL);(long long 类型) |
| 累乘 | std::reduce(v.begin(), v.end(), 1, std::multiplies<>()); |
| 自定义操作(Lambda) | std::reduce(v.begin(), v.end(), 初始值, [](a,b){ 自定义逻辑 }); |
| 并行累加 | std::reduce(std::execution::par, v.begin(), v.end(), 0); |