CSP202209.B何以包邮?

今天我们来看CSP202209.B何以包邮?这道题目

解题思路

问题分析

本题是一个典型的0-1背包问题变形。我们需要在 n 本书中选择若干本,使得总价格不小于 x,并且尽可能接近 x。

算法选择

  1. 暴力枚举:对于 n≤30,直接枚举所有子集的时间复杂度为 O(2^n),会超时。
  2. 折半枚举:将书分成两半,分别枚举,时间复杂度 O(2^(n/2)),可以接受。
  3. 动态规划:使用 0-1 背包思想,时间复杂度 O(n×sum),其中 sum 为总价格。

由于每本书价格 ai≤10^4,n≤30,所以总价格 sum≤3×10^5,动态规划完全可行。

动态规划思路

状态定义

  • dp[j] 表示能否通过选择若干本书,使得总价格恰好为 j。

状态转移

  • 初始状态:dp[0] = true,其余为 false
  • 对于每本书 a[i],从大到小遍历 j,更新 dp[j] = dp[j] || dp[j - a[i]]

答案查找

  • 从 x 开始向上遍历,找到第一个 dp[j] == true 的 j,即为满足条件的最小花费

复杂度分析

  • 时间复杂度:O(n × sum),其中 sum 为所有书价格总和
  • 空间复杂度:O(sum)

C++ 代码实现

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int n, x;
    cin >> n >> x;
    
    vector<int> a(n);
    int sum = 0;
    for (int i = 0; i < n; i++) {
        cin >> a[i];
        sum += a[i];
    }

    // dp[j] 表示能否凑出总价格恰好为 j
    vector<bool> dp(sum + 1, false);
    dp[0] = true;

    // 0-1背包:每本书只能选择一次
    for (int i = 0; i < n; i++) {
        // 从大到小遍历,避免重复选择同一本书
        for (int j = sum; j >= a[i]; j--) {
            if (dp[j - a[i]]) {
                dp[j] = true;
            }
        }
    }

    // 从 x 开始向上找第一个能凑出的价格
    for (int j = x; j <= sum; j++) {
        if (dp[j]) {
            cout << j << "\n";
            break;
        }
    }

    return 0;
}

代码详解

1. 输入处理

cpp 复制代码
int n, x;
cin >> n >> x;
vector<int> a(n);
int sum = 0;
for (int i = 0; i < n; i++) {
    cin >> a[i];
    sum += a[i];  // 计算总价格,用于确定 dp 数组大小
}

2. 动态规划数组初始化

cpp 复制代码
vector<bool> dp(sum + 1, false);
dp[0] = true;  // 不选择任何书时,总价格为 0

3. 0-1背包核心代码

cpp 复制代码
for (int i = 0; i < n; i++) {
    for (int j = sum; j >= a[i]; j--) {
        if (dp[j - a[i]]) {
            dp[j] = true;  // 如果 j-a[i] 可以凑出,那么 j 也可以凑出
        }
    }
}

注意:内层循环必须从大到小遍历,这样才能保证每本书最多被选择一次(0-1背包特性)。

4. 查找答案

cpp 复制代码
for (int j = x; j <= sum; j++) {
    if (dp[j]) {
        cout << j << "\n";
        break;  // 找到第一个满足条件的就输出
    }
}

示例运行

输入示例

复制代码
5 15
5
7
3
8
2

运行过程

  • 总价格 sum = 25
  • dp 数组大小为 26
  • 经过动态规划后,dp15 = true(可以选择 5+7+3 = 15)
  • 从 15 开始查找,找到 dp15 = true
  • 输出:15

注意事项

  1. 数组大小:dp 数组大小需要设为 sum+1,因为可能的最大价格是 sum
  2. 遍历顺序:内层循环必须从大到小,避免一本书被重复选择
  3. 边界条件:题目保证 sum≥x,所以一定能找到答案
  4. 优化:可以使用 bitset 优化空间和时间,但对于本题数据范围,bool 数组已经足够

总结

本题是经典的 0-1 背包问题变体,通过动态规划可以高效求解。关键点在于:

  1. 正确理解 0-1 背包的状态转移
  2. 掌握从大到小遍历的技巧
  3. 学会从 x 开始查找满足条件的最小值

这种方法时间复杂度为 O(n×sum),在给定数据范围内可以高效运行。

相关推荐
caimouse1 小时前
ReactOS 窗口系统分析(13):滚动条 — scrollbar.c + scrollex.c
c语言·开发语言·reactos
Sagittarius_A*1 小时前
【好靶场】PHP反序列化入门练习2
开发语言·web安全·信息安全·php·代码审计·反序列化
NoteStream1 小时前
MATLAB绘制带置信区间的折线散点图(科研绘图模板)
开发语言·matlab
tianyu2342 小时前
Java 正则表达式终极指南:从 Pattern 到 Matcher,从双重转义到性能优化,一篇全搞定
java·开发语言·正则表达式·group·find·pattern·matcher
caimouse4 小时前
ReactOS 窗口系统分析(23):键盘 — keyboard.c + kbdlayout.c
c语言·开发语言·计算机外设
Emily156853598705 小时前
Emerson 1C31129G03 Analog Input Module
java·开发语言·前端·plc·emerson·1c31129g03·input module
就叫_这个吧9 小时前
Java递归方法实现面包屑导航
java·开发语言
fīɡЙtīиɡ ℡10 小时前
AI 应用系统设计
java·开发语言·人工智能
今天AI了吗11 小时前
Python 基础语法(一):常量、变量、输入输出与运算符
开发语言·数据库·人工智能·python·sql·深度学习·机器学习