题解:学而思编程 假币购物

【题目来源】

学而思编程:假币购物

【题目描述】

战争时期,A国制造了大量敌国B国的假币,由间谍拿着假币到B国购买大宗商品。

B国的货币的面值有 \(n\) 种,第 \(i\) 种的面值为 \(m_i\)​,A国制造一张这样的假币的花费为 \(c_i\)​。

在B国有 \(k\) 件大宗商品可以购买,第 \(j\) 件商品的价格是 \(a_j\) ​,间谍购买时会支付总面值恰好等于价格的假币。

在造币总花费不超过 \(W\) 的前提下,最多能买到的总价格是多少?

【输入】

\(1\) 行,\(3\) 个正整数 \(n,k,W\)

接下来 \(n\) 行,每行 \(2\) 个正整数 \(m_i,c_i\)​,表示制造面值 \(m_i\) ​的假币需要的花费 \(c_i\)

最后一行,\(k\) 个正整数 \(a_1,a_2,⋯,a_k\)​,表示可以买的商品的价格。

【输出】

\(1\) 个整数,最多能买到的总价格。

【输入样例】

5 5 30
10 7
7 2
6 3
9 5
1 9
11 2 32 22 27

【输出样例】

59

【核心思想】

  1. 问题分析:给定 \(n\) 种假币面值 \(m_i\) 和制造成本 \(c_i\)\(k\) 件商品价格 \(a_j\),总预算 \(W\)。需要选择若干商品购买,使得制造对应面值假币的总成本不超过 \(W\),且商品总价格最大。这是一个完全背包 + 01背包的组合问题:第一阶段用完全背包计算制造每种面值的最小成本,第二阶段用01背包选择商品最大化总价格。

  2. 算法选择

    • 完全背包(第一阶段):计算制造总面值恰好为 \(j\) 的假币的最小花费 \(dp[j]\)
    • 01背包(第二阶段):在预算 \(W\) 内选择商品,最大化总价格 \(dp2[j]\)
    • 两阶段DP组合:第一阶段的结果作为第二阶段的成本输入
  3. 关键步骤

    • Step 1 - 完全背包预处理

      • 状态定义:\(dp[j]\) 表示制造总面值恰好为 \(j\) 的假币的最小花费
      • 初始化:memset(dp, 0x3f, sizeof(dp))\(dp[0] = 0\)
      • 状态转移:\(dp[j] = \min(dp[j], dp[j - m[i]] + c[i])\)(完全背包,从小到大遍历 \(j\)
      • 结果:\(dp[a[i]]\) 即为制造面值 \(a[i]\) 假币的最小成本
    • Step 2 - 01背包选择商品

      • 状态定义:\(dp2[i][j]\) 表示考虑前 \(i\) 件商品,花费恰好为 \(j\) 时的最大总价格
      • 不选第 \(i\) 件商品\(dp2[i][j] = dp2[i-1][j]\)
      • 选第 \(i\) 件商品:若 \(j \geq dp[a[i]]\)(预算足够),则 \(dp2[i][j] = \max(dp2[i][j], dp2[i-1][j - dp[a[i]]] + a[i])\)
      • 注意:购买第 \(i\) 件商品的成本是 \(dp[a[i]]\)(制造对应面值假币的最小花费)
    • Step 3 - 统计答案

      • 答案为 \(dp2[k][W]\),即在预算 \(W\) 内购买商品的最大总价格
  4. 时间/空间复杂度

    • 时间复杂度:\(O(n \times M + k \times W)\),其中 \(M\) 为完全背包的上限(最大商品价格)
    • 空间复杂度:\(O(M + k \times W)\),完全背包数组 \(dp[M]\) 和01背包数组 \(dp2[k][W]\)
  5. 完全背包 + 01背包组合的核心思想

    • 问题分解:将原问题分解为两个子问题——"制造假币的最小成本"和"在预算内选择商品"
    • 第一阶段(完全背包):预处理出所有可能面值的最小制造成本,为第二阶段提供成本数据
    • 第二阶段(01背包):将每件商品视为01背包中的物品,成本为第一阶段的计算结果,价值为商品价格
    • 状态衔接:第一阶段的 \(dp[a[i]]\) 直接作为第二阶段的状态转移参数
    • 最优子结构:两个阶段都满足最优子结构性质,组合后得到全局最优解
    • 适用于多阶段决策、成本预处理 + 选择优化的组合问题

【算法标签】

完全背包

【代码详解】

#include <bits/stdc++.h>
using namespace std;

// ================= 常量与全局变量 =================

const int N = 105;          // 最大面值种类数 / 最大商品数
const int M = 100005;       // 最大面值总额(用于完全背包)

int n;                      // 面值种类数
int k;                      // 商品数量
int W;                      // 总预算(造币花费上限)

int m[N];                   // 每种面值的面额
int c[N];                   // 制造一张该面值假币的花费

int a[N];                   // 每件商品的价格

int w[N];                   // 未使用的数组(保留原样)
int v[N];                   // 未使用的数组(保留原样)

int dp[N][M];               // 完全背包 dp 数组
                            // dp[i][j] 表示考虑前 i 种面值,制造总面值恰好为 j 的最小花费、

int dp2[N][M];              // 01背包 dp 数组
                            // dp2[i][j] 表示考虑前 i 件商品,花费恰好为 j 时的最大总价格

// ================= 主函数 =================
int main()
{
    // 读取面值种类数、商品数量和总预算
    cin >> n >> k >> W;

    // 读取每种面值的面额和制造成本
    for (int i = 1; i <= n; i++)
        cin >> m[i] >> c[i];

    // 读取每件商品的价格
    for (int i = 1; i <= k; i++)
        cin >> a[i];

    // ========= 第一阶段:完全背包 =========
    // 计算制造一定面值假币的最小花费

    // 初始化 dp 数组为无穷大
    memset(dp, 0x3f, sizeof(dp));
    dp[0][0] = 0;           // 制造 0 面值花费为 0

    // 完全背包:每种面值的假币可以无限制造
    for (int i = 1; i <= n; i++)
        for (int j = 0; j <= 100000; j++)
        {
            dp[i][j] = dp[i - 1][j];            // 不使用第 i 种面值
            if (j >= m[i])                       // 使用第 i 种面值
                dp[i][j] = min(dp[i][j], dp[i][j - m[i]] + c[i]);
        }

    // ========= 第二阶段:01背包 =========
    // 选择商品,最大化总价格

    // dp2[i][j] 表示考虑前 i 件商品,花费恰好为 j 时的最大总价格

    for (int i = 1; i <= k; i++)
    {
        for (int j = 0; j <= W; j++)
        {
            dp2[i][j] = dp2[i - 1][j];           // 不买第 i 件商品

            int cost = dp[n][a[i]];              // 制造价格为 a[i] 的假币所需的最小花费
            if (j >= cost)                       // 如果预算足够
                dp2[i][j] = max(dp2[i][j], dp2[i - 1][j - cost] + a[i]);  // 购买第 i 件商品
        }
    }

    // 输出在预算 W 内能买到的最大总价格
    cout << dp2[k][W] << endl;

    return 0;
}

【运行结果】

5 5 30
10 7
7 2
6 3
9 5
1 9
11 2 32 22 27
59
posted @ 2026-06-17 09:28  团爸讲算法  阅读(7)  评论(0)    收藏  举报