251题巧克力板的选择问题

166 阅读4分钟

答题实践笔记

题目解析

问题描述

小M有nn块巧克力板,每块巧克力的边长为aia_i,重量为ai2a_i^2。她有mm个背包,每个背包有一个最大承重限制QjQ_j。对于每个背包,她想知道在不超过背包承重的情况下,最多能带走多少块巧克力板。

思路分析

  1. 计算每块巧克力的重量:由于每块巧克力的重量为其边长的平方,我们需要先计算出所有巧克力的重量。

  2. 排序巧克力重量:将巧克力的重量从小到大排序,这样可以确保我们优先选择重量较轻的巧克力,从而在背包容量有限的情况下带走更多的巧克力。

  3. 前缀和计算:计算排序后巧克力重量的前缀和。前缀和数组中的第ii个元素表示前i+1i+1块最轻巧克力的总重量。

  4. 处理查询:对于每个背包的承重限制,我们可以使用二分查找(如upper_bound函数)在前缀和数组中找到不超过背包承重的最大巧克力数量。

图解

巧克力重量与背包承重关系图转存失败,建议直接上传图片文件

上图展示了巧克力重量的累积和与背包承重的关系。通过找到背包承重在累积重量曲线上的位置,可以确定最多能带走的巧克力数量。

代码详解

vector<int> solution(int n, int m, vector<int> a, vector<int> queries) {
    // 计算每块巧克力的重量
    vector<long long> weights(n);
    for(int i = 0; i < n; ++i) {
        weights[i] = (long long)a[i] * a[i];
    }

    // 将重量排序
    sort(weights.begin(), weights.end());

    // 计算前缀和
    vector<long long> prefix_sums(n);
    prefix_sums[0] = weights[0];
    for(int i = 1; i < n; ++i) {
        prefix_sums[i] = prefix_sums[i-1] + weights[i];
    }

    // 处理每个查询
    vector<int> result;
    for(int i = 0; i < m; ++i) {
        long long capacity = queries[i];
        // 使用upper_bound找到第一个前缀和大于背包承重的位置
        int count = upper_bound(prefix_sums.begin(), prefix_sums.end(), capacity) - prefix_sums.begin();
        result.push_back(count);
    }
    return result;
}

关键步骤说明

  • weights计算:为了避免整数溢出,将乘积转换为long long类型。
  • 排序sort函数对巧克力重量进行排序,确保从最轻的开始选择。
  • 前缀和计算:通过累加前面的重量,得到当前能携带的总重量。
  • upper_bound使用upper_bound函数在有序数组中查找第一个大于目标值的位置,这个位置的索引即为最多能携带的巧克力数量。

知识总结

二分查找与upper_bound

  • 二分查找:在有序数组中快速查找目标值的位置,时间复杂度为O(logn)O(\log n)
  • upper_bound:C++ STL中的函数,用于在有序序列中查找第一个大于给定值的位置。

学习建议:对于算法竞赛和实际开发,熟练掌握STL库中的算法函数能够大大提高编码效率和代码性能。建议初学者多练习二分查找的变体和应用。

前缀和技巧

  • 前缀和:通过预处理数组,使得在O(1)O(1)时间内计算任意区间的累加和。
  • 应用场景:适用于需要频繁计算区间和的问题。

学习建议:前缀和是基础且高效的算法技巧,熟练掌握后可以解决很多数组和序列相关的问题。

学习计划

制定刷题计划

  1. 明确目标:根据自己的水平和目标(如参加竞赛、面试等)制定合适的计划。
  2. 分配时间:每天固定时间练习,保持连续性。
  3. 分级练习:从简单题目开始,逐步挑战更高难度的题目。

利用错题进行针对性学习

  1. 记录错题:建立错题本,记录每次做错的题目和错误原因。
  2. 定期复习:定期回顾错题,巩固知识点。
  3. 深入思考:对于不理解的题目,查阅资料或请教他人,直到彻底明白。

高效学习方法

  • 主动思考:在看答案前,尽量自己思考解决方案。
  • 代码调试:多调试代码,理解每一行代码的作用。
  • 总结归纳:做完一类题目后,进行总结,找出通用的解题方法。

工具运用

结合AI与学习资源

  • 利用AI辅助:使用如ChatGPT等AI工具,帮助理解复杂的概念或代码。
  • 在线题库:结合LeetCode、牛客网等在线题库,进行系统性的练习。
  • 教学视频:观看算法教学视频,加深对算法思想的理解。

总结

通过这次题目的练习,我们不仅巩固了二分查找和前缀和的知识,还学会了如何高效地处理批量查询的问题。在学习过程中,善于利用各种工具和资源,能够事半功倍。希望这些经验对其他同学也有所帮助。 #青训营笔记创作活动