关于邮票收集de写法

邮票收集
小A是个邮票收集爱好家,他有n种面值的邮票,每种邮票都有无数张。一天小B想要寄信,需要一共面值和为k的邮票组合。小A想要知道拼出面值为k的邮票最少需要多少张。

时间限制:1000
内存限制:131072
输入
输入是多组数据。(不超过10组) 每组数据的第一行正整数n,k,表示邮票的种类数目和目标要拼出的钱。(0 < n ≤ 100, 0 < k ≤ 1000 ) 接下来的一行有n个正整数ai(0 < ai ≤ 1000)。 若n=k=0表示输入结束。
输出
每组数据输出一行一个数,分别表示拼出k需要的最少的邮票数量。 如果不存在能够拼出k的方案,输出-1。
样例输入
4 10
1 2 3 4
5 16
1 2 3 4 5
2 7
4 5
0 0
样例输出
3
4
-1
提示
第一组数据: 10 = 4+4+2 第二组数据:16 = 5+5+5+1 第三组数据: 不存在。

该回答引用GPTᴼᴾᴱᴺᴬᴵ
以下是C++的一种解法,使用动态规划,时间复杂度为 $O(nk)$。

#include <iostream>
#include <cstring>
using namespace std;

const int N = 110;
const int M = 1010;
const int INF = 0x3f3f3f3f;

int a[N], f[M];

int main()
{
    int n, k;
    while (cin >> n >> k, n || k)
    {
        memset(f, INF, sizeof f);
        f[0] = 0;
        for (int i = 0; i < n; i ++ )
            cin >> a[i];
        for (int i = 0; i < n; i ++ )
            for (int j = a[i]; j <= k; j ++ )
                f[j] = min(f[j], f[j - a[i]] + 1);
        if (f[k] == INF) cout << -1 << endl;
        else cout << f[k] << endl;
    }
    return 0;
}

参考GPT和自己的思路:

这是一个经典的动态规划问题,可以使用类似背包问题的方法解决。我们设dp[i][j]表示使用前i种邮票,凑出面值为j所需要的最少邮票数量。则有如下转移方程:

dp[i][j] = min(dp[i-1][j], dp[i][j-ai] + 1)

其中,dp[i-1][j]表示不使用第i种邮票的情况,dp[i][j-ai] + 1表示使用一张第i种邮票后剩余面值为j-ai的情况,需要再加上一张第i种邮票。最终答案为dp[n][k]。

需要注意的是,如果某个状态无法转移(即dp[i][j]无法由dp[i-1][j]和dp[i][j-ai] + 1转移而来),则应该设置其为一个较大的值,以表示其为不合法状态。如果最终dp[n][k]等于一个较大的值,则表示无法凑出面值为k的邮票组合,应该输出-1。

代码实现如下:

参考GPT和自己的思路,以下是C++实现的代码:

#include <iostream>
#include <cstring>
using namespace std;

const int MAXN = 105;
const int MAXK = 1005;
const int INF = 0x3f3f3f3f;

int n, k;
int a[MAXN];
int dp[MAXK];

int main() {
    while (cin >> n >> k && n && k) {
        memset(dp, INF, sizeof(dp));
        dp[0] = 0;
        for (int i = 0; i < n; i++) {
            cin >> a[i];
        }
        for (int i = 0; i < n; i++) {
            for (int j = a[i]; j <= k; j++) {
                dp[j] = min(dp[j], dp[j - a[i]] + 1);
            }
        }
        if (dp[k] == INF) {
            cout << -1 << endl;
        } else {
            cout << dp[k] << endl;
        }
    }
    return 0;
}

代码思路:

1.对于每组数据,首先用memset函数将dp数组全部初始化为正无穷。

2.将dp[0]赋值为0,表示拼出面值为0的邮票需要0张。

3.接下来依次输入n个邮票面值,并在每次输入时进行如下循环:
  从a[i]到k的范围内,将dp[j]更新为dp[j]和dp[j-a[i]]+1的最小值。
4.如果dp[k]的值仍然为正无穷,则说明无法拼出面值为k的邮票,输出-15.否则输出dp[k]的值,表示拼出面值为k的邮票最少需要dp[k]张。

参考gpt和自己的思路,代码如下:


#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int INF = 0x3f3f3f3f;
const int MAXN = 110;
const int MAXK = 1010;

int n, k;
int a[MAXN];
int dp[MAXK];

int main() {
    while (cin >> n >> k && n && k) {
        memset(dp, INF, sizeof(dp));
        dp[0] = 0;

        for (int i = 1; i <= n; ++i) {
            cin >> a[i];
            for (int j = a[i]; j <= k; ++j) {
                dp[j] = min(dp[j], dp[j-a[i]] + 1);
            }
        }

        if (dp[k] == INF) {
            cout << "-1\n";
        } else {
            cout << dp[k] << "\n";
        }
    }

    return 0;
}


代码中使用了动态规划的思想,定义了 dp[i] 表示拼出面值为 i 需要的最少邮票数。遍历每种面值的邮票,更新 dp 数组,最终得到拼出面值为 k 的最少邮票数。如果不存在能够拼出面值为 k 的方案,则输出 -1。