JAVA程式碼—演算法基礎:最少貨幣換錢問題求解(動態規劃)
最少貨幣換錢問題求解(動態規劃)
問題:換錢問題
給定一個數組arraydemo,arraydemo中所有的值都為正數且不重複。
每個值代表一種面值的貨幣,每種面值的貨幣可以使用任意張,再給定一個整數aim代表要找的錢數,
求組成aim的最少貨幣數。
舉例:
arraydemo=[1,5,10,50,100],aim = 53
1張50元,再加上3張1元,可以組成53元;其它的找錢方案都要使用更多張的貨幣,所以返回4
arraydemo=[1,5,10,50,100],aim=0
不用任何貨幣就可以組成0元,返回0
arraydemo=[5,10,50,100],aim=2
根本無法組成2元,錢不能找開的情況下預設返回-1
使用動態規劃求解思想分析過程:
原問題的分析為,如果arraydemo長度為N,生成行數為N,列數為aim+1的動態規劃表的矩陣DP。
dp[i][j]的含義是,在可以任意使用 arraydemo[0…i]貨幣的情況下,組成j所需的最小張數。
據此,dp[i][j]的值可以按照如下方式進行計算:
1、dp[0..N-1][0] 的值,即DP矩陣中第一列的值,表示找的錢數為0時需要的最少張數。錢數為0時,不需要任何貨幣,所以全設定為0即可。
2、dp[0][0..aim-1]的值,即DP矩陣中第一行的值,表示只能使用arraydemo[0]貨幣的情況下,找某個錢數的最小張數。
例如:arraydemo[0]=5,那麼能找開的錢數為 5,10,15,……,所以令 dp[0][5]=1, dp[0][10]=2, dp[0][15]=3,…,第一行其它位置所代表的錢數一律找不開,所以一律設定為32為整數的最大值,標記為max。
3、剩下的位置依次從左到右,再從上到下計算。
假設計算到位置:(i,j),dp[i][j]的值可能有:
(1) 完全不使用當前貨幣arraydemo[i]情況下的最少張數,即dp[i-1][j]的值
(2)只使用1張當前貨幣arraydemo[i]情況下的最少張數,即 dp[i-1][j-arraydemo[i]]+1
(3)只使用2張當前貨幣arraydemo[i]情況下的最少張數,即 dp[i-1][j-2*arraydemo[i]]+2
(4)只使用3張當前貨幣arraydemo[i]情況下的最少張數,即 dp[i-1][j-3*arraydemo[i]]+3
所有的情況中,最終取張數最小的。所以,
dp[i][j] = min(dp[i-1][j-k*arraydemo[i]]+k(0<=k))
推匯出; dp[i][j]=min(dp[i-1][j],min(dp[i-1][j-x*arraydemo[i]]+x(1<=x)))
再推匯出:dp[i][j]=min(dp[i-1][j],min(dp[i-1][j-arraydemo[i]-y*arraydemo[i]]+y+1(0<=y)))
又有:
min(dp[i-1][j-arraydemo[i]-y*arraydemo[i]]+y(0<=y)) => dp[i][j-arraydemo[i]]
所以,最終有:
dp[i][j]=min(dp[i-1][j],dp[i][j-arraydemo[i]]+1)
如果: j-arraydemo[i]<0,即發生了越界,說明 arraydemo[i]太大,用一張都會超過錢數j,令 dp[i][j]=dp[i-1][j]即可。
package com.bean.algorithmexec;
public class ExchangeDemo {
/*
* 問題:換錢問題
* 給定一個數組arraydemo,arraydemo中所有的值都為正數且不重複。
* 每個值代表一種面值的貨幣,每種面值的貨幣可以使用任意張,再給定一個整數aim代表要找的錢數,
* 求組成aim的最少貨幣數。
*
* 舉例:
* arraydemo=[1,5,10,50,100],aim = 53
* 1張50元,再加上3張1元,可以組成53元;其它的找錢方案都要使用更多張的貨幣,所以返回4
* arraydemo=[1,5,10,50,100],aim=0
* 不用任何貨幣就可以組成0元,返回0
* arraydemo=[5,10,50,100],aim=2
* 根本無法組成2元,錢不能找開的情況下預設返回-1
*
* */
private static int exchangeWays(int[] arraydemo, int aim) {
// TODO Auto-generated method stub
if(arraydemo==null || arraydemo.length==0 || aim<0) {
return -1;
}
int n=arraydemo.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-arraydemo[0]>=0 && dp[0][j-arraydemo[0]]!=max) {
dp[0][j]=dp[0][j-arraydemo[0]]+1;
}
}
int left=0;
for(int i=1;i<n;i++) {
for(int j=1;j<=aim;j++) {
left=max;
if(j-arraydemo[i]>=0 && dp[i][j-arraydemo[i]]!=max) {
left=dp[i][j-arraydemo[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) {
// TODO Auto-generated method stub
int arraydemo[]= {1,5,10,50,100};
int aim=57;
int answer= exchangeWays(arraydemo,aim);
System.out.println("# ANSWER= "+answer);
}
}
(完)