最少钱币数 - 动态规划法求解 - 可输出最终找零方案

最少钱币数

【问题描述】

这是一个古老而又经典的问题。用给定的几种钱币凑成某个钱数,一般而言有多种方式。例如:给定了6种钱币面值为25102050100,用来凑 15元,可以用52元、15元,或者35元,或者15元、110元,等等。显然,最少需要2个钱币才能凑成15元。

你的任务就是,给定若干个互不相同的钱币面值,编程计算,最少需要多少个钱币才能凑成某个给出的钱数。

之前使用了贪心法求解,贪心法是有一定条件才能成立的,具体什么条件,这里暂时不探讨,以后有机会我总结一下。

这个问题可以简述为:

如有 int coins[m] 个硬币,需要找零n(任意大的整数)钱币,那么如何做呢?

这就是需要动态规划法了,

假设cNums[i]是记录了需要的最少硬币数来找零i元的。那么如果我们使用面值是x的硬币来找零,那么现在我们就需要 cNums[i] = 1+cNums[i-x];记得i是总钱币数,需要减少x,然后找到了i-x下标,对应之前已经找到的最少钱币数的存储数组的值。

如下面的图:

每次只需要使用一个变量c存储当前行中最小值。12.8updat图表:

例子解说:

这里的初始化值是0,因为要找零0元,只需要0个硬币。

我们有2元7元和3元的硬币值。

当i=2的时候,那么就需要1个2元的硬币,到了3的时候就需要一个3元硬币,到了4需要2个2元硬币。以此类推,到了10可以用5个2元硬币,也可以用一个7元,一个3元,当然也可以先用3元再用7元,顺序不要紧,比较5和2,当然是2个硬币最少,所以最后就是2个硬币就可以找零10元了。

看程序:

int minChangeDP(int coins[], int m, int n, vector<int> &minCoins)
{
	if (n<0)
	{
		return -1;
	}
	vector<int> cNums(n+1,0);
	//注意:这里需要分配好内存
	minCoins.resize(n+1);
	int min;
	int c;
	//注意:这里的下标是1到n+1,因为第0个元素默认为零总钱币数,就只需要零张找零
	for (int i = 1; i < n+1; i++)
	{
		min = INT_MAX;
		c = 0;
		for (int j = 0; j < m; j++)
		{
			if (coins[j] <= i)
			{
				//注意:一定要有判断-1的条件,否则结果不正确
				//这里的意思是-1代表无法整数硬币找零,即无解!
				if (1+cNums[i-coins[j]] < min && cNums[i-coins[j]] != -1)
				{
					//注意:这里的min和c都是所有零钱比较过之后放入最小值才存在到cNums和minCoins中
					min = 1+cNums[i-coins[j]];
					c = j;
				}
			}
		}
		cNums[i] = min==INT_MAX? -1:min;
		minCoins[i] = c;
	}
	return cNums[n];
}


这里返回的是最少硬币数,其实可以更加强大,已经保存了所有n元以下的最少硬币找零的情况,有兴趣可以修改一下。

还可以输出最终方案呢:12.8 update : 减少coins[]元素个数的参数,不需要。

void makeChanges(int coins[], vector<int> &minCoins, int n)
{
	while (n>0)
	{
		cout<<coins[minCoins[n]]<<"\t";
		n = n - coins[minCoins[n]];//minCoins[]是硬币序列coins的下标
	}
	cout<<endl;
}


也可以输出所有n元一下的最少硬币找零情况。稍微修改一下就可以了。

下面是测试程序:

int main()
{
	int i;
	int arr1[] = {2};
	int arr2[] = {2,7};
	int arr3[] = {2,7,3};
	int m1 = sizeof(arr1)/sizeof(arr1[0]);
	int m2 = sizeof(arr2)/sizeof(arr2[0]);
	int m3 = sizeof(arr3)/sizeof(arr3[0]);
	//打印0-11元的所有最小硬币数
	for (i=0; i < 11; i++)
	{
		printf("%d  ", countDP(arr1, m1, i));
		printf("%d  ", countDP(arr2, m2, i));
		printf("%d  ", countDP(arr3, m3, i));
		cout<<endl;
	}
	
	cout<<endl;
	vector<int> minCoins;
	cout<<"Minimum changes is:\n";
	cout<<minChangeDP(arr3, m3, 999, minCoins);
	cout<<endl;

	cout<<"The changes we need is:\n";
	makeChanges(arr3, minCoins, 999);
	cout<<endl;

	system("pause");
	return 0;
}



运行结果,为了趣味,输出几个大点的方案看看:

10元找零方案:

100元:


下面会是多少元的找零方案呢?

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值