[PAT-A 1068]Find More Coins

在这里插入图片描述
题目大意:
有N枚硬币,给出每枚硬币的价值,现在要用这些硬币去支付价值为M的东西。
问是否可以找见这样的方案,使得选择用来支付的硬币价值之和恰好为M,如果不存在,输出NoSolution。
如果存在,从小到大输出选择用来支付硬币的价值,如果有多种方案,则输出字典序最小的那个。

思路:
题目中的价值c[i]与质量w[i]是等价的,可以把c[i]与w[i]使用一个数组存放。
由于题目要求以价值从小到大的顺序输出,因此需要先把数组从小到大排序,然后再进行正常的01背包的dp操作。
状态转移方程dp[i][v]=max{dp[i-1][v],dp[i-1][v-w[i]]+c[i]}.
在此基础上开设一个二维数组choice[i-1][v],用来计算dp[i][v]时是选择了哪个策略。
如果状态转移时选择了dp[i-1][v]则choice[i][v]=0,表示不放第i件物品。
如果在状态转移时选择了dp[i-1][v-w[i]]+c[i],则choice[i][v]=1,表示选择了第i件物品。
这样dp数组求解完之后,选择最大的dp[n][v],从第n件物品开始倒着查看每一件物品是否放入背包。

无解的条件为dp[n][m]!=m,因为题目要求恰好能付清价值为M的货币,因此只要dp[m]不是m就说明无解。
求解dp数组时,如果两个策略的大小相等,则应该选择放第i件物品的策略。

AC代码:

//PAT_A 1068
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn = 10010;
const int maxv = 110;
int w[maxn], dp[maxn][maxv] = { 0 };
bool choice[maxn][maxv], flag[maxn];
bool cmp(int a, int b) {
	return a > b;
}
int main() {
	int n, m;
	(void)scanf("%d %d", &n, &m);
	for (int i = 1; i <= n; i++) {
		(void)scanf("%d", &w[i]);
	}
	sort(w + 1, w + n + 1, cmp);
	for (int i = 1; i <= n; i++) {
		for (int v = w[i]; v <= m; v++) {
			int temp1 = dp[i - 1][v - w[i]] + w[i];//放
			int temp2 = dp[i - 1][v];//不放
			if (temp1 >= temp2) {//保证字典序,如果相等也要选
				choice[i][v] = 1;//选i号硬币
				dp[i][v] = temp1;
			}
			else {
				choice[i][v] = 0;//不选i号硬币
				dp[i][v] = temp2;
			}
		}
	}
	if (dp[n][m] != m)printf("No Solution");
	else {
		int k = n, num = 0, v = m;//看choice数组的第v列
		while (k >= 0) {
			if (choice[k][v] == 1) {
				flag[k] = true;
				v -= w[k];
				num++;
			}
			else flag[k] = false;
			k--;
		}
		for (int i = n; i >= 1; i--) {
			if (flag[i] == true) {
				printf("%d", w[i]);
				num--;
				if (num > 0)printf(" ");
			}
		}
	}
	return 0;
}
发布了142 篇原创文章 · 获赞 1 · 访问量 4552

猜你喜欢

转载自blog.csdn.net/weixin_44699689/article/details/104719162