1 条题解

  • 0
    @ 2026-6-19 10:30:04

    📝 题目大意

    给定 NN 个砝码,每个砝码 ii 的重量为 AiA_i。若一个正整数可以由不超过 33 个不同的砝码的重量之和表示,则称其为"好整数"。求在 [1,W][1, W] 范围内有多少个好整数。

    💡 解题思路

    1. 题目分析

      • N300N \le 300W106W \le 10^6Ai106A_i \le 10^6
      • 核心操作是从 NN 个元素中选 112233 个不同的元素求和,看哪些和值 W\le W 可以被覆盖。
      • NN 最大只有 300300O(N3)2.7×107O(N^3) \approx 2.7 \times 10^7 在可接受范围内,因此直接枚举所有组合是最简单且正确的做法。
    2. 算法推导

      • 使用布尔数组 visited 大小为 W+1W+1visited[x] = true 表示重量 xx 可以被拼出。
      • 选 1 个:遍历所有 AiA_i,若 AiWA_i \le W 则标记 visited[A_i] = true
      • 选 2 个:双重循环枚举所有无序对 (i,j)(i, j)i<ji < j),计算 sum=Ai+Ajsum = A_i + A_j,若 sumWsum \le W 则标记。
      • 选 3 个:三重循环枚举所有无序三元组 (i,j,k)(i, j, k)i<j<ki < j < k),计算 sum=Ai+Aj+Aksum = A_i + A_j + A_k,若 sumWsum \le W 则标记。
      • 最后遍历 1W1 \sim W,统计 visited[i]true 的个数即为答案。
    3. 边界与细节

      • 砝码重量可能相同(如样例 3),但题目要求不同的砝码(下标不同),所以即使 AiA_i 值相同,只要下标不同就视为不同砝码。枚举时用下标 i<j<ki < j < k 保证了这一点。
      • 求和时只关心 sumWsum \le W,超过 WW 的直接忽略,无需标记(数组大小只有 W+1W+1,避免越界)。
      • 注意 visited 数组大小设置为 W+1W+1,下标从 11WW 有意义。

    ⏱️ 复杂度分析

    • 时间复杂度O(N3)O(N^3),其中枚举 11 个、22 个、33 个砝码的组合分别需要 O(N)O(N)O(N2)O(N^2)O(N3)O(N^3),总复杂度由 O(N3)O(N^3) 主导。N=300N=300 时约 2.7×1072.7 \times 10^7 次运算,可以通过。
    • 空间复杂度O(W)O(W),即 visited 数组的大小。W106W \le 10^6,约 11 MB,完全可行。

    💻 标准代码 (C++)

    #include <iostream>
    #include <vector>
    
    using namespace std;
    
    int main() {
        // 优化输入输出效率
        ios_base::sync_with_stdio(false);
        cin.tie(NULL);
    
        int N, W;
        if (!(cin >> N >> W)) return 0;
    
        vector<int> A(N);
        for (int i = 0; i < N; ++i) {
            cin >> A[i];
        }
    
        // visited[x] 用于标记重量 x 是否可以被拼凑出来
        // 因为只需要统计不超过 W 的好整数,所以数组大小设为 W + 1 即可
        vector<bool> visited(W + 1, false);
    
        // 1. 选择 1 个砝码
        for (int i = 0; i < N; ++i) {
            if (A[i] <= W) {
                visited[A[i]] = true;
            }
        }
    
        // 2. 选择 2 个不同的砝码(枚举所有无序对 (i, j),i < j)
        for (int i = 0; i < N; ++i) {
            for (int j = i + 1; j < N; ++j) {
                int sum = A[i] + A[j];
                if (sum <= W) {
                    visited[sum] = true;
                }
            }
        }
    
        // 3. 选择 3 个不同的砝码(枚举所有无序三元组 (i, j, k),i < j < k)
        for (int i = 0; i < N; ++i) {
            for (int j = i + 1; j < N; ++j) {
                for (int k = j + 1; k < N; ++k) {
                    int sum = A[i] + A[j] + A[k];
                    if (sum <= W) {
                        visited[sum] = true;
                    }
                }
            }
        }
    
        // 统计在 1 到 W 范围内有多少个被标记的好整数
        int ans = 0;
        for (int i = 1; i <= W; ++i) {
            if (visited[i]) {
                ans++;
            }
        }
    
        cout << ans << "\n";
    
        return 0;
    }
    
    • 1

    信息

    ID
    597
    时间
    1000ms
    内存
    256MiB
    难度
    10
    标签
    (无)
    递交数
    6
    已通过
    2
    上传者