邮票收集
小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的邮票,输出-1。
5.否则输出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。