2025.12.01 力扣每日一题

2141.同时运行N台电脑的最长时间

"困难",灵茶山艾府解法,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());
  • 说明:默认初始值是「元素类型的默认值」(如 int0float0.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 不支持
操作要求 需满足「结合律 + 交换律」(如加法、乘法) 仅需结合律(如字符串拼接)
初始值 可选(默认用类型默认值) 必须提供

四、注意事项

  1. 操作的结合律 / 交换律 :并行模式下,reduce 会打乱元素顺序执行操作,因此操作必须满足「结合律 + 交换律」(如加法、乘法)。如果用减法、除法(不满足交换律),结果会不确定

  2. 初始值类型 :若序列是 float/long long,初始值要对应类型(如 0LL 对应 long long),避免类型不匹配。

  3. 头文件依赖

    • 基础用法:需包含 <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::arraystd::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;
}

并行计算注意事项

  1. 必须包含头文件 <execution>
  2. 自定义操作必须满足「结合律 + 交换律」(如加法、乘法),否则结果不确定(例如减法、字符串拼接不能用并行);
  3. 适合处理「无状态、独立元素」的计算(如数值累加、累乘),大规模数据下优势明显。

四、兼容模板(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);
相关推荐
爱装代码的小瓶子1 小时前
【cpp知识铺子】map和set的前身-二叉搜索树
c++·算法
TL滕1 小时前
从0开始学算法——第四天(练点题吧)
数据结构·笔记·学习·算法
[J] 一坚1 小时前
华为OD、微软、Google、神州数码、腾讯、中兴、网易有道C/C++字符串、数组、链表、树等笔试真题精粹
c语言·数据结构·c++·算法·链表
多则惑少则明1 小时前
【算法题4】找出字符串中的最长回文子串(Java版)
java·开发语言·数据结构·算法
迷途之人不知返1 小时前
二叉树题目
数据结构·算法
优宁维生物2 小时前
DNA 提取的基础方法
人工智能·算法
@Aurora.3 小时前
优选算法【专题二:滑动窗口】
算法
小石头 100863 小时前
【Java】String类(超级详细!!!)
java·开发语言·算法
.柒宇.3 小时前
力扣hot100---42.接雨水(java版)
java·算法·leetcode