题目描述:

思路:参考自《程序员代码面试指南》--左程云著

这是一道经典的动态规划方法,我们可以构造一个dp数组,如果arr的长度为N,则dp数组的行数为N,列数为aim+1,dp[i][j] 的含义是:在可以任意使用arr[0..i]货币的情况下,组成j所需要的最小张数。

明白以上定义后我们初始化第一行与第一列,第一行dp[0][0..aim]中每一个元素dp[0][j]表示用arr[0]货币找开面额 j所需要的最少货币数,此时我们只能选取arr[0]这一张货币,所以只有arr[0]的整数倍的面额钱才可以找开,例如当arr[0]=3,aim=10时,只能找开3,6,9的货币,而其他面额的则无法找开,所以将arr[0][3,6,9]初始化为1,2,3 除此之外其他值初始化为整形int的最大值INT_MAX表示无法找开。对于第一列dp[0..n][0] 中的每一个元素dp[i][0]表示用arr[i]组成面额为0的钱的最少货币数,完全不需要任何货币,直接初始化为0即可。

对于剩下的任意dp[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-karr[i]]+k )( k>=0 )
=>dp[i][j] = min{ dp[i-1][j], min{ dp[i-1][j-x
arr[i]]+x (1<=x) } } 接下来令x = y+1
=>dp[i][j] = min{ dp[i-1][j], min{ dp[i-1][j-arr[i]-yarr[i]+y+1 (0<=y) ] } }
又有 min{ dp[i-1][j-arr[i]-y
arr[i]+y (0<=y) ] } => dp[i][ j-arr[i] ] ,(这步是这么来的:这个式子与上面的式子只相差了个1,说明这个式子少用了一个arr[i]。表达过来就是使用前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]。

代码:

class Solution {
    public int coinChange(int[] coins, int amount) {
        int len = coins.length;
        int[][] dp = new int[len][amount+1];
        
        //初始化第0行,也就是只用coins[0]时
        for(int i=1;i<amount+1; i++){
            dp[0][i] = Integer.MAX_VALUE;
            if(i>=coins[0]&&dp[0][i-coins[0]]!=Integer.MAX_VALUE){
                dp[0][i] = dp[0][i-coins[0]]+1;
            }
        }
        
        for(int i = 1; i< len; i++){
            for(int j=1; j< amount+1; j++){
                int left = Integer.MAX_VALUE;
                if(j>=coins[i] && dp[i][j-coins[i]]!=Integer.MAX_VALUE){
                    left = dp[i][j-coins[i]]+1;
                }
                dp[i][j] = Math.min(dp[i-1][j],left);
            }
        }
        
        return dp[len-1][amount] == Integer.MAX_VALUE?-1:dp[len-1][amount];
    }
}