分享一个大牛的人工智能教程。零基础!通俗易懂!风趣幽默!希望你也加入到人工智能的队伍中来!请轻击人工智能教程
- 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 ChangeMoneyMinCurrencyNumber1 {
-
- 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 = {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
- */