題目
假設我們有8種不同面值的硬幣{1,2,5,10,20,50,100,200},用這些硬幣組合夠成一個給定的數值n。例如n=200,那麼一種可能的組合方式爲 200 = 3 * 1 + 1*2 + 1*5 + 2*20 + 1 * 50 + 1 * 100. 問總過有多少種可能的組合方式?原題 轉載
分析
這道題目是非常經典的動態規劃算法題。給定一個數值sum,假設我們有m種不同類型的硬幣
求所有可能的組合數,就是求滿足前面等值的係數
思路1:
用暴力枚舉,各個係數可能的取值無非是
,這對於硬幣種類數較小還可以應付。
思路2:
從上面的分析中我們也可以這麼考慮,我們希望用m種硬幣構成sum,根據最後一個硬幣
…
其中
定義dp[i][sum] = 用前i種硬幣構成sum 的所有組合數。
那麼題目的問題實際上就是求dp[m][sum],即用前m種硬幣(所有硬幣)構成sum的所有組合數。
在上面的聯合等式中,當
其中
初始情況:如果sum=0,那麼無論有前多少種來組合0,只有一種可能,就是各個係數都等於0,
如果我們用二位數組表示dp[i][sum], 我們發現第i行的值全部依賴與i-1行的值,所以我們可以逐行求解該數組。如果前0種硬幣要組成sum,我們規定爲dp[0][sum] = 0。
思路3:硬幣組合問題,本質上就是組合數的問題,解決組合問題,非常經典的算法是回溯算法,它在無限的解空間中深度優先搜索。
第一種動態規劃算法實現如下:
import java.util.Scanner;
/**
* 有幾種紙幣面值1, 5, 10, 20, 50, 100元,假設每種面值的紙幣無限,用它們組合成N元。找出所有的組合數。
* @author ShaoCheng
* @version 1.0 2015-9-19
*/
public class Solution {
/**
* @param N 輸入的總和N
* @return 所有的組合數
*/
public long getNumberOfCombinations(int N) {
int coinKinds = coins.length;
int[][] dp = new int[coinKinds+1][N+1];
for(int i = 0; i <= coinKinds; i++){ //初始化
for(int j = 0; j <= N; ++j){
dp[i][j] = 0;
}
}
for(int i = 0; i <= coinKinds; i++){
dp[i][0] = 1;//前i種紙幣組合成0,只有一種情況就是個數均爲0
}
for(int i = 1; i <= coinKinds; i++){
for(int j = 1; j <= N; j++){
dp[i][j] = 0;
for(int k = 0; k <= j / coins[i-1]; k++){
dp[i][j] += dp[i-1][j - k*coins[i-1]];
}
}
}
return dp[coinKinds][N];
}
public static void main(String[] args){
Solution sl = new Solution();
Scanner scanner = new Scanner(System.in);
int N = scanner.nextInt();
long res = sl.getNumberOfCombinations(N);
System.out.println(res);
scanner.close();
}
public Solution() {
// TODO Auto-generated constructor stub
coins = new int[]{1, 5, 10, 20, 50, 100};
}
private int[] coins;
}
回溯算法實現如下:
import java.util.Arrays;
import java.util.Scanner;
/**
* 有幾種紙幣面值1, 5, 10, 20, 50, 100元,假設每種面值的紙幣無限,用它們組合成N元。找出所有的組合數。
* @author ShaoCheng
* @version 1.1 2015-9-20
*/
public class Solution {
/**
* @param N 輸入的總和N
* @return 所有的組合數
*/
public long getNumberOfCombinations(int N) {
long sum = 0;
return getNumberOfCombinations(N, sum, 0);
}
public long getNumberOfCombinations(int N, long sum, int start){
long count = 0;
for(int i = start; i < coins.length; i++){
sum += coins[i];
if(sum == N){
count++;
break;
}
if(sum < N){
count += getNumberOfCombinations(N, sum, i);
sum -= coins[i];
}
else
break;
}
return count;
}
public static void main(String[] args){
Solution sl = new Solution();
Scanner scanner = new Scanner(System.in);
int N = scanner.nextInt();
long res = sl.getNumberOfCombinations(N);
System.out.println(res);
scanner.close();
}
public Solution() {
// TODO Auto-generated constructor stub
coins = new int[]{1, 5, 10, 20, 100, 50}; //如果亂序,應先排序
Arrays.sort(coins);
}
private int[] coins;
}