有 n 种不同面值的硬币,每种硬币有无限多个。为了方便购物,他希望带尽量 少的硬币,但是要能组合出 1 到 m 之间的任意值。 //第一行为两个整数:m 和 n,他们的意义如题目描述。接下来的 n 行

题目:有 n 种不同面值的硬币,每种硬币有无限多个。为了方便购物,他希望带尽量 少的硬币,但是要能组合出 1 到 m 之间的任意值。第一行为两个整数:m 和 n,他们的意义如题目描述。接下来的 n 行,每行一个整数,第 i + 1 行的整数表示第 i 种硬币的面值最少需要携带的硬币数量,如果无解则输出-1。

输入:
20 4
1 2 5 10
输出:
5

分析:sum表示当前能凑到的最大面值(即1~sum的面值都凑到了),当sum超过m,就停止(m是题上要求凑1~m的面值)a[i]里存的是不同的面值将面值数组排序后,没有面值是1的必然无解,而且面值1必选。对于1~m,凑的时候先凑小的,因为小的多了可以满足大的。此外还有一个思想就是如果当前1~sum面值凑够了,那么下一步应该考虑凑sum+1的面值,具体过程是在排好序的面值数组中,从大到小找,找一个元素a[i](面值<=sum+1的)加等到sum上,这意味着1~(sum+a[i])面值凑齐了,这里找面值<=sum+1的寓意是保证能够凑齐的面值从sum+1面值开始到sum+a[i]面值是连续的。(例如某个时刻sum等于5,那么说明下一个要凑的面值是6,此时我们在面值数组中从大->小找<=6的元素(面值1,2,5,10),我们找到了5,那么由于sum等于5,说明1~5的面值已经凑齐了,此时如果再添加一个面值是5的硬币,那么我们是不是就可以凑齐1~10了,这里不仅凑齐了6,还多凑齐了7,8,9,10,但是要是取一个大于6的比如是10,那么1~5凑齐了,再加上一个10,那么6~10将无法凑齐,而如果我们选择2,那么1~5凑齐了,再加上2,相当于凑齐了1~7,这比起选择5明显不是最优解,因为选择5直接可以凑齐1~10),选择该面值后就将count加1(存最终结果的变量),此时sum加等5后变成10,意味着1~10已经凑齐了,下一次需要凑的是11,再以相同的思想,看能不能选一个面值,直接不仅能够凑齐11,还能多凑一些,比如12,13...,就这样直到sum的值大于等于我们要求的值m时,说明1~m的所有面值我们都能凑齐了,此时输出count,就是我们需要凑齐1~m所有面值所需的最少硬币数.。

#include <algorithm>
int min_core()
{
	int a[1005] = {0};
	int n = 0;//不同面值的种类数
	int m = 0;//要凑齐1~m的面值

	//根据面值种类个数n,手动初始化面值数组
	cin >> m >> n;
	for (int i = 1; i <= n; i++)
	{
		cin >> a[i];
	}

	//对面值数组排序(默认是升序)
	sort(a + 1, a + 1 + n);

	//如果没有1面值,没有解
	if (a[1] != 1)
	{
		return -1;
	}

	int sum = 0;//目前已经凑够了1~sum面值的硬币了
	int count = 0;//记录要选取的硬币个数
	while (1)
	{
		//如果1~m已经凑齐了,返回count
		if (sum >= m)
		{
			return count;
		}

		for (int i = n; i >= 1; i--)
		{
			//面值数组从大->小找一个面值<=sum+1的,
			//说明该硬币我们要了,则count加加
			if (a[i] <= sum + 1)
			{
				sum += a[i];//此时1~(sum+a[i])的面值都能够凑齐了,因此更新sum
				count++;
				break;//更新sum后,直接上去判断此时的sum是不是满足m了
			}
		}
	}
}

猜你喜欢

转载自blog.csdn.net/lyl194458/article/details/89057461