硬币改变

原文地址 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​​​​​​​

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值