题解:洛谷 P14920 道具商店

【题目来源】

洛谷:P14920 [GESP202512 六级] 道具商店 - 洛谷

【题目描述】

道具商店里有 \(n\) 件道具可供挑选。第 \(i\) 件道具可为玩家提升 \(a_i\) 点攻击力,需要 \(c_i\) 枚金币才能购买,每件道具只能购买一次。现在你有 \(k\) 枚金币,请问你最多可以提升多少点攻击力?

【输入】

第一行,两个正整数 \(n,k\),表示道具数量以及你所拥有的金币数量。

接下来 \(n\) 行,每行两个正整数 \(a_i,c_i\),表示道具所提升的攻击力点数,以及购买所需的金币数量。

【输出】

输出一行,一个整数,表示最多可以提升的攻击力点数。

【输入样例】

3 5
99 1
33 2
11 3

【输出样例】

132

【核心思想】

  1. 问题分析:给定 \(n\) 件道具,每件道具提供攻击力 \(a_i\),花费金币 \(c_i\),总预算 \(k\) 枚金币,每件只能购买一次。求预算内可获得的最大攻击力。这是一个01 背包问题,但采用逆向 DP 思路——将"攻击力"作为重量、"金币"作为成本,求获得指定攻击力所需的最小成本。

  2. 算法选择

    • 01 背包(逆向 DP)\(dp[j]\) 表示获得总攻击力 \(j\) 所需的最小金币数
    • 答案提取:在所有可达的攻击力 \(j\) 中,找到成本 \(\leq k\) 的最大 \(j\)
  3. 关键步骤

    • 初始化\(dp[0] = 0\)(获得 \(0\) 攻击力成本为 \(0\)),其余 \(dp[j] = \infty\)
    • 读入数据\(n\)(道具数)、\(k\)(金币数)、\((a_i, c_i)\)(攻击力和成本)
    • 01 背包 DP(遍历 \(i\)\(1\)\(n\)):
      • 更新总攻击力上限 \(sum = \sum_{p=1}^{i} a_p\)
      • 倒序遍历 \(j\)\(sum\)\(a_i\)
        • 状态转移\(dp[j] = \min(dp[j], dp[j - a_i] + c_i)\)(不选或选第 \(i\) 件道具)
    • 提取答案:遍历 \(j\)\(0\)\(sum\),若 \(dp[j] \leq k\)\(ans = \max(ans, j)\)
    • 输出答案 \(ans\)
  4. 时间/空间复杂度

    • 时间复杂度:\(O(n \times \sum a_i)\),其中 \(\sum a_i\) 为总攻击力上限
    • 空间复杂度:\(O(\sum a_i)\),一维 \(dp\) 数组
  5. 01 背包的核心思想

    • 逆向思维转换:当传统背包(容量 \(\to\) 最大价值)的容量 \(k\) 过大时,可将"价值"作为重量、"重量"作为成本,求"获得指定价值的最小重量"
    • 倒序遍历保证每件只选一次\(j\) 从大到小遍历,确保 \(dp[j - a_i]\) 是未选第 \(i\) 件时的状态
    • 状态压缩:一维数组滚动更新,\(dp[j]\) 同时承担"不选"(原值)和"选"(\(dp[j-a_i]+c_i\))两种状态的比较
    • 答案域枚举:DP 完成后遍历所有可能的攻击力值,找到预算约束下的最大值
    • 适用于容量过大但价值范围有限的背包问题、资源分配、成本优化类问题

【解题思路】

img/Pasted image 20260511150334.png

【算法标签】

普及 #01背包

【代码详解】

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

#define int long long
const int N = 505;
int n, k;  // n: 物品数量, k: 预算上限
int a[N];  // 存储每个物品的重量
int c[N];  // 存储每个物品的成本
int dp[N * N];  // 动态规划数组,dp[j]表示获得总重量j的最小成本
int sum;  // 所有物品的总重量
int ans;  // 最终答案,在预算内能获得的最大总重量

signed main()
{
    // 初始化动态规划数组,所有值设为无穷大(表示不可达状态)
    memset(dp, 0x3f, sizeof(dp));
    dp[0] = 0;  // 获得总重量0的成本为0

    cin >> n >> k;  // 输入物品数量和预算上限

    // 输入每个物品的重量和成本
    for (int i = 1; i <= n; i++)
    {
        cin >> a[i] >> c[i];
    }

    // 动态规划:01背包问题
    for (int i = 1; i <= n; i++)
    {
        // 更新当前物品能获得的总重量范围
        sum += a[i];

        // 从后向前遍历,确保每个物品只使用一次
        for (int j = sum; j >= a[i]; j--)
        {
            // 状态转移:选择当前物品或不选择当前物品
            // dp[j] = min(不选当前物品, 选当前物品)
            dp[j] = min(dp[j], dp[j - a[i]] + c[i]);
        }
    }

    // 在所有可能的重量中,找到成本不超过预算的最大重量
    for (int j = 0; j <= sum; j++)
    {
        if (dp[j] <= k)  // 如果获得重量j的成本不超过预算
        {
            ans = max(ans, j);  // 更新最大重量
        }
    }

    cout << ans << endl;  // 输出结果

    return 0;  // 程序正常结束
}

【运行结果】

3 5
99 1
33 2
11 3
132
posted @ 2026-08-10 12:27  团爸讲算法  阅读(21)  评论(0)    收藏  举报