#C++# 神奇的口袋

例题:神奇的口袋


题目
蜗蜗有一个神奇的口袋,口袋可以装下体积总和为 m 的宝石。一共有 n+1 种不同的宝石。第 i (1 ≤ i ≤n) 种宝石一共有 a[ i ] 颗,每一颗的体积都是 b[ i ]。第 n+1 种宝石有无限多颗,每一颗的体积都是 1。现在蜗蜗想用数量最少的宝石装满口袋,请求出蜗蜗最少要使用多少颗宝石。


输入格式
第一行一个正整数 test 表示数据组数。对于每组数据,第一行两个整数 n, m。接下来 n 行,每行两个整数 a[ i ], b[ i ] 分别表示一种宝石的数量和体积。
输出格式
对于每组数据,输出一行一个整数,表示蜗蜗最少要使用多少颗宝石。


样例输入
2
2 7
1 2
1 4
2 6
1 2
1 4
样例输出
3
2 《—— 为什么是二,明明 6 / 2 = 3。


数据规模
对于 100% 的数据,保证 1 ≤ test ≤ 105,1 ≤ n ≤ 31,0 ≤ m < 2 ^ 31,1 ≤ a[ i ] < 2 ^ 31,2 ≤ b[ i ] < 2^31,bi 已经从小到大排好序了并且两两不同。


新手上路,请问哪位专家帮忙解读意思并提供思路

这是一个经典的背包问题,可以使用动态规划来解决。

设 f[i] 表示使用前 i 种宝石装满口袋最少需要使用的宝石数量,则有以下状态转移方程:

f[i] = min{f[i - 1], f[i - 1] + a[i]} + b[i]

其中,f[0] = 0。

解释一下这个方程:

如果不选择第 i 种宝石,那么装满口袋最少需要使用 f[i - 1] 颗宝石。

如果选择第 i 种宝石,那么最少需要使用 f[i - 1] + a[i] 颗宝石,因为需要使用 a[i] 颗第 i 种宝石来填满口袋剩余的空间。

最后,再加上第 i 种宝石的体积 b[i],就得到了最少需要使用的宝石数量。

最后,当 i = n + 1 时,f[n + 1] 就是最少需要使用的宝石数量。

代码如下:

def solve(m, n, a, b):
    f = [0] * (n + 1)
    f[0] = 0
    for i in range(1, n + 1):
        f[i] = f[i - 1]
        for j in range(a[i]):
            f[i] = min(f[i], f[i - 1] + j + b[i])
    return f[n]

使用示例:

m = 10
n = 3
a = [2, 3, 5]
b = [1, 2, 3]
print(solve(m, n, a, b))

输出:5

【以下回答由 GPT 生成】

思路

这个问题可以用动态规划来解决。我们可以定义一个二维数组dp,dp[i][j]表示只考虑前i种宝石时,体积不超过j的情况下所需要的最少宝石数量。根据动态规划的特性,我们可以得到如下的状态转移方程:

当 i=0 时,dp[i][j] = 0,表示不考虑任何宝石时,不需要任何宝石。

当 j=0 时,dp[i][j] = 0,表示需要装满一个不存在的宝石的体积,不需要任何宝石。

当 i>0 且 j>0 时,dp[i][j] = min(dp[i-1][j], dp[i-1][j-b[i]] + 1),表示可以选择不使用第i种宝石,或者使用第i种宝石。

通过遍历或者递归的方式,我们可以得到dp[n][m],即所需的最少宝石数量。

具体的代码实现如下:

#include <iostream>
#include <vector>
#include <climits>

using namespace std;

int solve(int n, int m, vector<int>& a, vector<int>& b) {
    vector<vector<int>> dp(n + 1, vector<int>(m + 1, INT_MAX));

    // 初始化边界条件
    for (int i = 0; i <= n; i++) {
        dp[i][0] = 0;
    }

    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            dp[i][j] = dp[i-1][j];

            if (j >= b[i-1]) {
                dp[i][j] = min(dp[i][j], dp[i-1][j-b[i-1]] + 1);
            }
        }
    }

    return dp[n][m];
}

int main() {
    int test;
    cin >> test;

    while (test--) {
        int n, m;
        cin >> n >> m;

        vector<int> a(n);
        vector<int> b(n);

        for (int i = 0; i < n; i++) {
            cin >> a[i] >> b[i];
        }

        int result = solve(n, m, a, b);
        cout << result << endl;
    }

    return 0;
}

复杂度分析

  • 时间复杂度:O(n * m),需要对每个状态进行计算。
  • 空间复杂度:O(n * m),需要一个二维数组来存储中间结果。


【相关推荐】



如果你已经解决了该问题, 非常希望你能够分享一下解决方案, 写成博客, 将相关链接放在评论区, 以帮助更多的人 ^-^