【算法1-动态规划】硬币找零最优解问题

(算法1-动态规划-学习整理存档)

1.问题:

给定一个整数数组 int coins[] 表示不同面额的硬币, int amount表示总金额,返回找零所需的最少硬币数量。如果总金额为0,可以由0枚硬币组成。如果这笔钱不能由任何硬币组成,返回-1。硬币数量无限制。

输入:硬币面值(种类不限),总金额

输出:找零所需的最少硬币数量

Problem:

You are given an integer array coins representing coins of different denominations and an
integer amount representing a total amount of money.
Return the fewest number of coins that you need to make up that amount, including the
amount 0 which can be made up of 0 coin. If that amount of money cannot be made up by any
combination of the coins, return -1.
You may assume that you have an infinite number of each kind of coin.

2.算法思路

问题分析:

硬币找零求最优解问题,可由若干交叠的子问题构成,且符合递推关系。贪心算法只能选择局部最优,所以并不适用;递归算法考虑了整体最优,可以解决问题,但穷举所有组合的计算效率太低;而动态规划算法,可以减少重复运算,并得到全局最优解。因此,本问题宜选取动态规划算法解决。

动态规划实现思路:

1)状态:找出子问题与原问题之间会发生变化的变量,这个变量就是状态转移方程的参数。

在硬币找零问题中,此变量为剩余兑换金额。

2)状态转移方程:F[i] 表示兑换金额为 i 时,需要的最少硬币数, 边界为 i =amount ;

         F(i)= 0,  n=0;

         F(i)= -1, n<0;

         F(i)= min( F(i), F(n-coins[j])+1 )

        其中 coin[j] 表示第j种硬币的面值。

3)初始状态:动态规划是由已知的子问题递推到更大问题上去,因此需要一个初始状态作为原点,成为计算的开端;

在本问题中,初始状态为 amount=0 时 F(0)=0。

4)执行状态转移:改变状态,让状态不断接近初始状态。

在本问题中,选择一枚硬币,用其凑零钱,即可改变状态。

5)返回最终结果。

3.算法复杂度分析

时间效率:O(nm)

空间效率:θ(n)

其中n为兑换总金额,m为硬币面值种类。

算法可表示为表格,每行有n个格子表示F(n), 每一格的计算需要求出至多m个数的最小值。

4.源代码

#include <iostream>
#include<vector>
using namespace std;

/*	动态规划
	1.设计状态
	2.写出状态转移方程
	3.设定初始状态
	4.执行状态转移
	5.返回最终的解
*/

/*算法函数*/
int MinCoinNum(vector<int>coins,int amount,int coin_type)
{
	vector<int>coinNum;//存储i面值,找零所需硬币个数
	vector<int>coinUsed;//存储所用的硬币
	//初始化
	for (int k = 0; k <= amount; k++)
	{
		coinNum.push_back(0);
		coinUsed.push_back(0);
	}
	//特殊情况0
	if (0 == amount)
	{
		return 0;
	}
    //递推开始
	for (int i = 1; i <= amount; i++)
	{
		int coin_min = INT_MAX;//i面值,需要硬币个数
		int coin_last = 0;//当前使用的硬币
		//限制硬币面值
		for (int j = 0; j < coin_type && coins[j] <= i; j++)
		{
			//判断i-coins[j]能否找开
			if (i == coins[j] || coinUsed[i - coins[j]] != 0)
			{
				coin_min = (coinNum[i - coins[j]] + 1) <= coin_min ? coinNum[i - coins[j]] + 1 : coin_min;
				coin_last = coins[j];//更新
			}
		}
		coinNum[i] = coin_min;
		coinUsed[i] = coin_last;
	}
	if (0 == coinUsed[amount])//找不开
	{
		return -1;
	}
	else
	{
		return coinNum[amount];
	}
}


int main()
{
	vector<int>coins;
	int amount;
	int coin_type = 0;

	cout << "Please input denominations." << endl;
	int tp;
	while (cin >> tp )
	{
		coins.push_back(tp);
		coin_type++;
		if (cin.get() == '\n')
			break;
	}
	cout << "Please input amount." << endl;
	cin >> amount;
	cout << "Result:" << endl;
	cout << MinCoinNum(coins, amount, coin_type) << endl;

	return 0;
}

5.结果

9cf5b6e91ee9455e87c30c1037f5cb9f.png

facedb566f6246728a2457bc1fc12ee1.png

3790bf84f1e34b6b8ad814b13841b0d3.png

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值