PAT-ADVANCED1068/Data Structures and Algorithms7-24——Find More Coins

我的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++解题报告:

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值