硬币兑换问题

描述:给定总金额为A 的一张纸币,现要兑换成面额分别为a1, a2,…, an 的硬币。硬币兑换问题是用最少枚数的硬币来兑换总金额为A 的纸币。

如 a = {1, 2, 5},兑换6元则要2枚硬币(1和5)。根据特定的a,有可能可以直接用贪心算法每次都选取可用的最大硬币值。但对于一些例子,会出现错误情况。

如 a = {1, 4, 5},兑换8元。若直接选取最大值,则需要4枚(1个5和3个1),但显然最优值应为2枚(2个4)。

 

对于兑换n元的硬币的最优情况,可以遍历列表 a ,分别选取1个其面值的硬币,再从中挑出最优结果,此时 f(n) = min{f(n-ai)+1}。而每个之前算法的结果都可能被后面的结果重复使用。满足动态规划中的最优子结构和重叠子问题。

 

算法基本思路:

1.计算最小枚数;

如前所述,设 f[i] 表示兑换 i 元所需要的最小枚数硬币,初始化 f[0] = 0.

f[i] = min{f[i-a[j]] + 1}, 其中 i >= a[j], 0 <= j < a.length

 

2.构造最小枚数所需的各个硬币。

在上面的基础上,设 rest[i] 表示经过本次兑换后所剩下的面值,即 i - rest[i] 可得到本次兑换的硬币值。

从 k = n 开始,逆推可构建整个路线(k = rest[k])。

 

public class ChangeCoins{

    public static int getChanges(int[] a, int n){
        /*
        a: all kinds of coins
        minNum: the min number of coins
        rest: the rest money after using this coin
        */
        if(n < 0)
            return -1;
        //init
        int[] minNum = new int[n+1];
        int[] rest = new int[n+1];
        minNum[0] = 0;
        //dynamic programming
        for(int i = 1; i <= n; i ++){
            int min = i;
            for(int j = a.length-1; j >= 0; j --){
                if(i >= a[j] && min > 1 + minNum[i-a[j]]){
                    min = 1 + minNum[i-a[j]];
                    rest[i] = i-a[j];
                }
            }
            minNum[i] = min;
        }
        //build the path
        int k = n;
        while(rest[k] != 0){
            System.out.print((k-rest[k]) + " ");
            k = rest[k];
        }
        System.out.println(k);
        return minNum[n];
    }

    public static void main(String[] args) {
        int[] a = {1, 4, 5};
        int n = 8;
        System.out.println(n + ": ");
        System.out.println(getChanges(a, n));
    }
    
}
Java

 

posted @ 2013-12-07 16:38  7hat  阅读(526)  评论(0编辑  收藏  举报