251 巧克力板选择问题 | 豆包MarsCode AI刷题

55 阅读3分钟

巧克力板选择问题

问题描述

小M想携带尽可能多的巧克力板参加春游。她有 n 块巧克力板,每块巧克力的边长为 ai,重量等于边长的平方。小M有多个背包,每个背包都有最大承重限制。我们需要帮助她计算在每个背包的承重范围内,最多可以携带多少块巧克力板。

示例输入输出

  • 输入: n = 5,边长数组 a = [1, 2, 2, 4, 5],背包承重限制 queries = [1, 3, 7, 9, 15]。
  • 输出: [1, 1, 2, 3, 3]

每个背包的承重限制对应的最大可携带巧克力块数量分别为 1、1、2、3、3。


问题分析

解题思路

  1. 重量计算:将每块巧克力的边长平方,得到重量。
  2. 排序与前缀和:将巧克力按重量从小到大排序,并计算其前缀和,方便快速求解任意承重下的最大块数。
  3. 二分查找优化:对于每个背包的最大承重,使用二分查找快速定位最多可携带的巧克力块数。

详细步骤解析

1. 巧克力重量计算

每块巧克力的重量为其边长的平方:

  • 如果边长为 1,则重量为 1^2 = 1。
  • 如果边长为 2,则重量为 2^2 = 4。

我们将所有重量存入数组,方便后续处理。

2. 排序与前缀和

对重量数组排序后,通过前缀和快速计算任意范围内的总重量。前缀和数组的定义为: prefixSum[i]=j=1iweights[j]prefixSum[i] = \sum_{j=1}^{i}{weights[j]}这样我们可以快速获取任意前缀的总重量。

3. 二分查找优化

使用二分查找在前缀和数组中定位不超过背包承重的最大重量位置,从而确定最多可携带的巧克力数量。二分查找将查询的时间复杂度从 O(n) 降低到 O(log⁡ n)。


代码实现

import java.util.Arrays;

public class Main {

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

        // 排序重量数组
        Arrays.sort(weights);

        // 计算前缀和
        int[] prefixSum = new int[n + 1];
        for (int i = 0; i < n; i++) {
            prefixSum[i + 1] = prefixSum[i] + weights[i];
        }

        // 处理每个查询,使用二分查找
        int[] result = new int[m];
        for (int i = 0; i < m; i++) {
            int maxWeight = queries[i];
            result[i] = binarySearch(prefixSum, maxWeight);
        }

        return result;
    }

    private static int binarySearch(int[] prefixSum, int maxWeight) {
        int left = 0, right = prefixSum.length - 1;
        while (left <= right) {
            int mid = (left + right) / 2;
            if (prefixSum[mid] <= maxWeight) {
                left = mid + 1;
            } else {
                right = mid - 1;
            }
        }
        return right;
    }

    public static void main(String[] args) {
        System.out.println(Arrays.equals(solution(5, 5, new int[]{1, 2, 2, 4, 5}, new int[]{1, 3, 7, 9, 15}), new int[]{1, 1, 2, 3, 3}));
        System.out.println(Arrays.equals(solution(4, 3, new int[]{3, 1, 2, 5}, new int[]{5, 10, 20}), new int[]{2, 2, 3}));
        System.out.println(Arrays.equals(solution(6, 4, new int[]{1, 3, 2, 2, 4, 6}, new int[]{8, 12, 18, 25}), new int[]{2, 3, 4, 4}));
    }
}

代码讲解

  1. 计算重量: 对每块巧克力计算其重量并存储在数组 weights 中。
  2. 排序与前缀和: 对重量排序,并通过循环计算前缀和,构建 prefixSum 数组。
  3. 二分查找: 对每个查询使用二分查找,快速找到满足条件的巧克力数量。

思考与优化

  • 时间复杂度: 排序的时间复杂度为 O(nlog⁡ n),每次查询使用二分查找的时间复杂度为 O(log⁡ n)。因此,总体复杂度为 O(nlog ⁡n+mlog⁡ n),适合处理大量数据。
  • 优化方向: 如果背包数量远大于巧克力数量,可以考虑使用多线程或并行处理查询,进一步提升性能。

总结

这道题涉及到经典的前缀和与二分查找结合的应用。通过这次练习,我更加深刻地理解了如何在排序数组中高效查询。这种思路在解决背包问题、动态规划等领域都有广泛应用。同时,也让我意识到代码结构清晰、思路清楚的重要性。