AcWing 1022. 宠物小精灵之收服

题目传送门

一、题目解析

题目描述
傻东西皮神 一起去抓 宝可梦

一共会遇到 \(n\)野生宝可梦,小智有 \(m\)精灵球,皮神有 \(t\)

对于每个 野生宝可梦 来说,如果要捕捉他,需要 \(v_{1i}\)精灵球,战斗后皮神要掉 \(v_{2i}\)

如果皮神 血量 \(≤0\),则小智停止狩猎

小智对于每个 野生宝可梦 要么收服,要么逃跑

求一种方案:收服 尽可能多野生宝可梦 ;如果收服数量一样,要皮神 血量最多

输出该方案的 野生宝可梦 收服数量,以及皮神战斗结束后的 剩余血量

分析

y总在标签里很贴心的贴了一个** 阅读理解** 的tag,没错这就是一道阅读理解题

本题是一道 01背包 的扩展题 —— 二维费用01背包问题

野生宝可梦 看做物品,则捕捉他需要的 精灵球 个数就是第一费用,战斗皮神要掉的血就是第二费用

最后答案要求物品数量最多,因此我们可以用状态的属性来表示选择的物品数

以上就是本题的** 阅读理解分析部分** ,接下来直接上闫氏DP分析法

闫氏DP分析法

初始状态:f[0][0][0]
目标状态:f[n][m][t - 1] (皮神不能 再起不能 才算抓住那个 宝可梦

二、实现代码

时间复杂度:\(O(n×m×k)\)

#include <bits/stdc++.h>

using namespace std;

const int N = 110;  //野生小精灵的数量
const int M = 1010; //小智的精灵球数量
const int K = 510;  //皮卡丘初始的体力值

int n, m, t;
int v1[N], v2[N];

int f[M][K];//一维:精灵球数量,二维:皮卡丘的体力值

int main() {
    //input
    cin >> m >> t >> n;
    //收服该小精灵需要的精灵球的数量,以及收服过程中对皮卡丘造成的伤害
    for (int i = 1; i <= n; i++) cin >> v1[i] >> v2[i];

    //dp
    //多维费用01背包
    //降维需要将体积1、体积2倒序枚举
    //注意血不能全用了,需要留一个点
    for (int i = 1; i <= n; i++)
        for (int j = m; j >= v1[i]; j--)
            for (int k = t - 1; k >= v2[i]; k--)
                f[j][k] = max(f[j][k], f[j - v1[i]][k - v2[i]] + 1);

    //最多收服多少个小精灵
    cout << f[m][t - 1] << " ";
    //找到满足最大价值的所有状态里,第二维费用消耗最少的
    int cost = t - 1;
    for (int k = 0; k <= t - 1; k++)
        if (f[m][k] == f[m][t - 1]) cost = min(cost, k);

    //收服最多个小精灵时皮卡丘的剩余体力值最大是多少
    cout << t - cost << endl;
    return 0;
}

原文地址:https://www.cnblogs.com/littlehb/p/15666368.html