换钱的最小货币数-Java:原问题的经典动态规划方法

分享一个大牛的人工智能教程。零基础!通俗易懂!风趣幽默!希望你也加入到人工智能的队伍中来!请轻击http://www.captainbed.net

package live.every.day.ProgrammingDesign.CodingInterviewGuide.RecursionAndDynamicPrograming;

/**
 * 换钱的最小货币数
 *
 * 【题目】
 * 给定数组arr,arr中所有的值都为正数且不重复。每个值代表一种面值的货币,每种面值的货币可以使用任意张,再给定一个整数aim
 * 代表要找的钱数,求组成aim的最小货币数。
 *
 * 【补充题目】
 * 给定数组arr,arr中所有的值都为正数。每个值仅代表一张钱的面值,再给定一个整数aim代表要找的钱数,求组成aim的最小货币数。
 *
 * 【难度】
 * 中等
 *
 * 【解答】
 * 原问题的经典动态规划方法。如果arr的长度为N,生成行数为N、列数为aim+1的动态规划表的dp。dp[i][j]的含义是,在可以任意
 * 使用arr[0..i]货币的情况下,组成j所需的最小张数。根据这个定义,dp[i][j]的值按如下方式计算:
 * 1、dp[0..N-1][0]的值(即dp矩阵中第一列的值)表示找的钱数为0时需要的最小张数,钱数为0时,完全不需要任何货币,所以
 * 全设为0即可。
 * 2、dp[0][0..aim]的值(即dp矩阵中第一行的值)表示只能使用arr[0]货币的情况下,找某个钱数的最小张数。比如,arr[0]=2,
 * 那么能找开的钱数为2,4,6,8,...所以令dp[0][2]=1,dp[0][4]=2,dp[0][6]=3,...第一行其他位置所代表的钱数一律找不开,
 * 所以一律设为32位整数的最大值,我们把这个值记为max。
 * 3、剩下的位置依次从左到右,再从上到下计算。假设计算到位置(i,j),dp[i][j]的值可能来自下面的情况。
 * 完全不使用当前货币arr[i]情况下的最少张数,即dp[i-1][j]的值。
 * 只使用1张当前货币arr[i]情况下的最少张数,即dp[i-1][j-arr[i]]+1。
 * 只使用2张当前货币arr[i]情况下的最少张数,即dp[i-1][j-2*arr[i]]+2。
 * 只使用3张当前货币arr[i]情况下的最少张数,即dp[i-1][j-3*arr[i]]+3。
 * 所有的情况中,最终取张数最小的,所以
 * dp[i][j]=min{dp[i-1][j-k*arr[i]]+k(0<=k)}
 * =>dp[i][j]=min{dp[i-1][j],min{dp[i-1][j-x*arr[i]]+x(1<=x)}}
 * =>dp[i][j]=min{dp[i-1][j],min{dp[i-1][j-arr[i]-y*arr[i]]+y+1(0<=y)}}
 * 又有min{dp[i-1][j-arr[i]-y*arr[i]]+y(0<=y)} => dp[i][j-arr[i]],所以,最终有:
 * dp[i][j]=min{dp[i-1][j],dp[i][j-arr[i]]+1}。如果j-arr[i]<0,即发生越界了,说明arr[i]太大,用一张都会超过
 * 钱数j,令dp[i][j]=dp[i-1][j]即可。具体过程请参看如下代码中的minCoins1方法,整个过程的时间复杂度与额外空间复杂度
 * 都为O(N*aim),N为arr的长度。
 *
 * @author Created by LiveEveryDay
 */
public class ChangeMoneyMinCurrencyNumberSolution1 {

    public static int minCoins1(int[] arr, int aim) {
        if (arr == null || arr.length == 0 || aim < 0) {
            return -1;
        }
        int n = arr.length;
        int max = Integer.MAX_VALUE;
        int[][] dp = new int[n][aim + 1];
        for (int j = 1; j <= aim; j++) {
            dp[0][j] = max;
            if (j - arr[0] >= 0 && dp[0][j - arr[0]] != max) {
                dp[0][j] = dp[0][j - arr[0]] + 1;
            }
        }
        int left = 0;
        for (int i = 1; i < n; i++) {
            for (int j = 1; j <= aim; j++) {
                left = max;
                if (j - arr[i] >= 0 && dp[i][j - arr[i]] != max) {
                    left = dp[i][j - arr[i]] + 1;
                }
                dp[i][j] = Math.min(left, dp[i - 1][j]);
            }
        }
        return dp[n - 1][aim] != max ? dp[n - 1][aim] : -1;
    }

    public static void main(String[] args) {
        int[] arr = {5, 2, 5, 3};
        int aim = 10;
        System.out.printf("The min currency number is: %d", minCoins1(arr, aim));
    }

}

// ------ Output ------
/*
The min currency number is: 2
*/

你可能感兴趣的:(#,Coding,Interview,Guide,换钱的最小货币数,原问题的经典动态规划方法)