我的PAT-ADVANCED代码仓:https://github.com/617076674/PAT-ADVANCED
我的Data Structures and Algorithms代码仓:https://github.com/617076674/Data-Structures-and-Algorithms
原题链接:
PAT-ADVANCED1068:https://pintia.cn/problem-sets/994805342720868352/problems/994805402305150976
Data Structures and Algorithms7-24:https://pintia.cn/problem-sets/16/problems/686
题目描述:
PAT-ADVANCED1068、Data Structures and Algorithms7-24:
题目翻译:
1068 寻找更多硬币
伊娃喜欢收集来自宇宙各处的硬币,包括其他一些像火星一样的行星。有一天,她参观了一个可以接受各种硬币作为付款的通用购物中心。但是,付款有一个特殊要求:对于每张帐单,她必须支付确切的金额。由于她有多达10 ^ 4个硬币,她需要你的帮助。对于任何给定数额的钱,你需要告诉她,她是否可以找到一些钱来支付它。
输入格式:
每个输入文件包含一个测试用例。对于每个测试用例,第一行包含2个正数:N(<= 10 ^ 4,代表硬币总数)和M(<= 10 ^ 2,代表伊娃必须支付的金额)。第二行包含硬币的N个面值,它们都是正数。一行中的所有数字都用空格分隔。
输出格式:
对每个测试用例,在一行中打印硬币面值使得V1 <= V2 <= ... <= Vk且V1 + V2 + ... + Vk = M。一行中的所有数字由一个空格分开,行末不得有多余空格。如果这样的组合不是唯一的,输出最小序列。如果找不到任何解决方案,输出“No Solution”。
注意:如果存在k > 1对任意A[i] = B[i],(i < k),满足A[k] < B[k],则称序列{A[1], A[2], ...}小于序列{B[1], B[2], ...}。
输入样例1:
8 9
5 9 8 7 2 3 4 1
输出样例1:
1 3 5
输入样例2:
4 8
7 2 4 3
输出样例2:
No Solution
知识点:动态规划
思路:01背包问题
此题本质上是一个01背包问题,只不过该背包的价值和质量是相同的。
由于题目要求以价值从小到大的字典序顺序输出,因此需要先把数组从大到小排序,然后再进行正常求解01背包的dp数组的操作。
状态定义:
f(x, y) -------- 考虑[0, x]个硬币装进容量为y的容器里所能组成的最大价值
状态转移:
当x == 0时,如果y值装得下索引为0的硬币,则其最大价值就是nums[0],否则是0。
当x > 0时,f(x, y) = max{f(x - 1, y), f(x - 1, y - nums[x]) + nums[x])。如果两种策略的大小相等,选择后者,即选择放第x件物品的策略。
在此基础上,我们需要开一个bool型的二维数组choice[x][y],用来记录计算f(x, y)时是选择了哪个策略。如果在状态转移时选择了f(x - 1, y),那么记choice[x][y] = false,否则记choice[x][y] = true。
这样,当动态规划求解完毕后,就可以从第n - 1件物品开始倒着查看每一件物品是否放入背包。
C++代码:
#include<iostream>
#include<algorithm>
using namespace std;
bool cmp(int a, int b);
int main(){
int N, M;
scanf("%d %d", &N, &M);
int nums[N];
for(int i = 0; i < N; i++){
scanf("%d", &nums[i]);
}
sort(nums, nums + N, cmp);
int dp[N][M + 1];
bool choice[N][M + 1];
for(int i = 0; i < M + 1; i++){
if(nums[0] <= i){
dp[0][i] = nums[0];
choice[0][i] = true; //选择了索引为0的硬币
}else{
dp[0][i] = 0;
choice[0][i] = false; //不选择索引为0的硬币
}
}
for(int i = 1; i < N; i++){
for(int j = 0; j < M + 1; j++){
if(j - nums[i] >= 0 && dp[i - 1][j - nums[i]] + nums[i] >= dp[i - 1][j]){
dp[i][j] = dp[i - 1][j - nums[i]] + nums[i];
choice[i][j] = true; //选择索引为i的硬币
}else{
dp[i][j] = dp[i - 1][j];
choice[i][j] = false;
}
}
}
if(dp[N - 1][M] != M){
printf("No Solution\n");
return 0;
}
bool flag[N];
int value = M, count = 0;
for(int i = N - 1; i >= 0; i--){
if(choice[i][value]){
flag[i] = true;
value -= nums[i];
count++;
}else{
flag[i] = false;
}
}
for(int i = N - 1; i >= 0; i--){
if(flag[i]){
printf("%d", nums[i]);
count--;
if(count > 0){
printf(" ");
}
}
}
return 0;
}
bool cmp(int a, int b){
return a > b;
}
C++解题报告: