题目:有 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了
}
}
}
}