原文地址 http://www.daileinote.com/computer/math/10
硬币改变(Coin Change)其实是动态规划里的组合问题,给出一个数 N ,在可选的数组S里 {s1,s2,s3...},有多少种组合的方式,使得它们的和等于 N
N = 4
S = {1,2,3}
4种解决方案 {1,1,1,1},{1,1,2},{2,2},{1,3}
N = 10
S = {2, 5, 3, 6}
有 5 种 {2,2,2,2,2}, {2,2,3,3}, {2,2,6}, {2,3,5}, {5,5}
算法分析
最优子结构(Optimal Substructure)
可以分解成2步
1.不包含某个数字
2.至少包含一次某个数字
此时我们可以设计一个函数 func(S, len, N) = func(S, len-1, N) + func(S, len, N - S[len-1])
重叠子问题(Overlapping Subproblems)
下面是递归实现此算法,子问题有重复计算
#include <stdio.h>
int conbination_sum(int *arr, int len, int sum)
{
printf("{%d %d},", len, sum);
// 和为0的组合有一种,即空组合
if (sum == 0)
return 1;
// 和为负数,没有这种组合
if (sum < 0)
return 0;
//数组为空,而和又是大于0,没有这种组合
if (len <= 0 && sum >= 1)
return 0;
// 返回不包含最后一个数,或者至少包含一个最后一个数的所有组合的可能
return conbination_sum(arr, len - 1, sum) + conbination_sum(arr, len, sum - arr[len-1]);
}
int main()
{
int arr[] = {1,2,3};
int len = sizeof(arr) / sizeof(int);
printf("\nconbination sum: %d\n", conbination_sum(arr, len, 4));
return 0;
}
{3 4},{2 4},{1 4},{0 4},{1 3},{0 3},{1 2},{0 2},{1 1},{0 1},{1 0},{2 2},
{1 2},{0 2},{1 1},{0 1},{1 0},{2 0},{3 1},{2 1},{1 1},{0 1},{1 0},{2 -1},{3 -2},
conbination sum: 4
从结果可以看出,很多相同的子问题都被计算了多次比如 1,1
跟其他动态规划问题一样,这里满足 最优子结构 和 重叠子问题,可以构建 arr[][]解决
下面是利用记忆法
#include <stdio.h>
int table[10][1024];
int conbination_sum(int *arr, int len, int sum)
{
// 和为0的组合有一种,即空组合
if (sum == 0)
return 1;
// 和为负数,没有这种组合
if (sum < 0)
return 0;
//数组为空,而和又是大于0,没有这种组合
if (len <= 0 && sum >= 1)
return 0;
if (table[len][sum])
return table[len][sum] - 1;
printf("{%d %d},", len, sum);
// 返回不包含最后一个数,或者至少包含一个最后一个数的所有组合的可能
int csum = conbination_sum(arr, len - 1, sum) + conbination_sum(arr, len, sum - arr[len-1]);
table[len][sum] = csum + 1;
return csum;
}
int main()
{
int arr[] = {500,400,200};
int len = sizeof(arr) / sizeof(int);
printf("\nconbination sum: %d\n", conbination_sum(arr, len, 1000));
return 0;
}
下面是利用表格法
#include <stdio.h>
#include <string.h>
int count( int S[], int m, int n )
{
// table[i] will be storing the number of solutions for
// value i. We need n+1 rows as the table is constructed
// in bottom up manner using the base case (n = 0)
int table[n+1];
// Initialize all table values as 0
memset(table, 0, sizeof(table));
// Base case (If given value is 0)
table[0] = 1;
// Pick all coins one by one and update the table[] values
// after the index greater than or equal to the value of the
// picked coin
for(int i=0; i<m; i++)
for(int j=S[i]; j<=n; j++)
table[j] += table[j-S[i]];
return table[n];
}
int main()
{
int arr[] = {2, 5, 10};
int len = sizeof(arr) / sizeof(int);
printf("\nconbination sum: %d\n", count(arr, len, 10));
return 0;
}
原文地址 http://www.daileinote.com/computer/math/10