动态规划算法求解硬币找零问题


硬币找零问题描述:现存在一堆面值为 V1、V2、V3 … 个单位的硬币,问最少需要多少个硬币才能找出总值为 T 个单位的零钱?假设这一堆面值分别为 1、2、5、21、25 元,需要找出总值 T 为 63 元的零钱。

很明显,只要拿出 3 个 21 元的硬币就凑够了 63 元了。

基于上述动态规划的思想,我们可以从 T=1 开始计算出最少需要几个硬币,然后再求 T=2 、T=3…每一次求得的结果都保存在一个数组中,以后需要用到时则直接取出即可。

从 T=1 开始依次找零时,可以尝试一下当前要找零的面值 T 是否能够被分解成 另一个已求解的面值的找零需要的硬币个数 再加上 这一堆硬币中的某个面值之和,如果这样分解之后最终的硬币数是最少的,那么问题就得到答案了。

单是上面的文字描述太抽象,先假定以下变量:

values[] : 保存每一种硬币的币值的数组
valueKinds :币值不同的硬币种类数量,即values[]数组的大小
money : 需要找零的面值
dp[] : 保存面值为 i 的纸币找零所需的最小硬币数

算法描述:

当求解总面值为 i 的找零最少硬币数 dp[i] 时,将其分解成求解 dp[i – cents] 和一个面值为 cents 元的硬币,由于 i – cents < i , 其解 dp[ i – cents] 已经在之前的循环中被算出,如果面值为 cents 的硬币满足题意,那么最终解 dp[i] 则等于 dp[i – cents] 再加上 1(即面值为 cents)的这一个硬币。

public class CoinsChange {
	/**
	 * 硬币找零:动态规划算法
	 * @param values:保存每一种硬币的币值的数组        
	 * @param valueKinds:币值不同的硬币种类数量,即coinValue[]数组的大小         
	 * @param money:需要找零的面值       
	 * @param dp:保存面值为i的纸币找零所需的最小硬币数          
	 */
	public static void makeChange(int[] values, int valueKinds, int money, int[] dp) {
		dp[0] = 0;
		// 对每一分钱都找零,即保存子问题的解以备用,即填表
		for (int cents = 1; cents <= money; cents++) {
			// 当用最小币值的硬币找零时,所需硬币数量最多
			int minCoins = cents;
			// 遍历每一种面值的硬币,看是否可作为找零的其中之一
			for (int kind = 0; kind < valueKinds; kind++) {
				// 若当前面值的硬币小于当前的cents则分解问题并查表
				if (values[kind] <= cents) {
					int temp = dp[cents - values[kind]] + 1;
					if (temp < minCoins) {
						minCoins = temp;
					}
				}
			}
			// 保存最小硬币数
			dp[cents] = minCoins;
			System.out.println("面值为 " + (cents) + " 的最小硬币数 : " + dp[cents]);
		}
	}

	public static void main(String[] args) {
		// 硬币面值预先已经按降序排列
		int[] coinValue = new int[] { 25, 21, 10, 5, 1 };
		// 需要找零的面值
		int money = 63;
		// 保存每一个面值找零所需的最小硬币数,0号单元舍弃不用,所以要多加1
		int[] dp = new int[money + 1];
		makeChange(coinValue, coinValue.length, money, dp);
	}
}
上面的代码并没有给出具体应该是哪几个面值的硬币,这个可以再使用一些数组保存而打印出来。

猜你喜欢

转载自blog.csdn.net/huanghanqian/article/details/79838326