title: 硬币组合
date: 2018-4-5 10:12:40
categories:
- algorithm
tags:
- algorithm
问题
给定一个数值sum,假设我们有m种不同类型的硬币{V1, V2, …, Vm},如果要组合成sum,求所有可能的组合数。
给定一个数值sum,假设我们有m种不同类型的硬币{V1, V2, …, Vm},如果要组合成sum
sum = x1*v1 + x2*v2 + x3*v3 + ..... + xm*vm
求所有可能的组合数,求满足所有等值的系数{x1,x2,x3,...xm}
暴力破解
枚举,每个系数取值
x1 = {0,1,2….sum/v1}
x2 = {0,1,2….sum/v2}
.
.
.
.
复杂度很高
动态规划
设vm硬币取值 0-m
sum = x1*v1 + x2*v2 + x3*v3 + .... + 0*vm
sum = x1*v1 + x2*v2 + x3*v3 + .... + 1*vm
sum = x1*v1 + x2*v2 + x3*v3 + .... + 2*vm
sum = x1*v1 + x2*v2 + x3*v3 + .... + 3*vm
.......
sum = x1*v1 + x2*v2 + x3*v3 + .... + k*vm
k = sum/vm
dp[i][sum] = 前i种硬币构成sum的所有组合数
dp[i][sum] =
dp[i-1][sum - 0*Vm] + dp[i-1][sum - 1*Vm]+ dp[i-1][sum - 2*Vm] + … + dp[i-1][sum - K*Vm]; 其中K = sum / Vm
一般例子解答:
假设我们有3种不同面值的硬币{1,2,5},用这些硬币组合够成一个给定的数值n。
问总过有多少种可能的组合方式?保证n小于等于100000。
public static void main(String[] args) {
for (int i = 1; i < 10; i++) {
System.out.println(coin_count(i));
}
}
public static int coin_count (int n) {
int[] coins = {1,2,5};
int[] dp = new int[100000];
dp[0] = 1;
for (int i = 0; i < coins.length; i++) {
for (int j = coins[i]; j <= n; j++) {
dp[j] = dp[j] + dp[j - coins[i]];
}
}
return dp[n];
}
结果:
1
2
2
3
4
5
6
7
8
硬币问题的变幻
现在给定入肝硬币, 例如{1,2,5},sum = 11 result = 3; 11 = 5+5+1 求组合成sum的最少的硬币数,如果无法组合,例如 {2}, sum = 3 ; result = -1.
dp[0] = 0;
dp[1] = 1;
dp[2] = min(dp[2], dp[2 - 1] + 1, dp[2 - 2] + 1) = dp[0] + 1 = 1;
dp[3] = min(dp[3], dp[3 - 1] + 1, dp[3 - 2] + 1) = dp[1] + 1 = 2;
........
dp[n] = min(dp[n], dp[n - coins[j]] + 1) : coins[j] <= n;
public static int coinChange(int[] coins, int amount) {
if (coins == null || coins.length == 0 || amount <= 0) {
return 0;
}
if (coins.length == 1) {
}
int[] dp = new int[amount + 1];
Arrays.fill(dp, 100000);
dp[0] = 0;
for (int i = 1; i <= amount; i++) {
for (int j = 0; j < coins.length; j++) {
if (i >= coins[j]) {
dp[i] = Math.min(dp[i], dp[i - coins[j]]+ 1);
}
}
}
return dp[amount] == 100000?-1:dp[amount];
}