UVA 357 Let me count the ways

题目点我
本来是道简单的题目,主要想写下优化的过程。
给你无限多面值为1,5,10,25,50的硬币,问你用他们组成价值为 M 的组合方法数是多少。用Fk(v)表示用前 k 种硬币组成v的方法总数,可以写出递推方程:

Fk(v)=i=0icoin[k]vFk1(vicoin[k])

直接按照递推方程写代码的话要三重循环:分别枚举 k,v,i ,时间复杂度 O(KVI) ,空间复杂度 O(KV) 。但是可以利用计算顺序来优化时空复杂度。看下面的矩阵:
F1(0)F2(0)Fk1(0)Fk(0)F1(1)F2(1)Fk1(1)Fk(1)F1(v1)F2(v1)Fk1(v1)Fk(v1)F1(v)F2(v)Fk1(v)Fk(v)

计算 Fk(v) 时需要上一行,即 Fk1(v),vv 的所有值,所以计算顺序应该是左上到右下,即三重循环都是递增的。
把一开始的递推方程变型,得到
Fk(v)=Fk1(v)+Fk1(vcoin[k])+Fk1(v2coin[k])++Fk1(vncoin[k])=Fk1(v)+Fk(vcoin[k])

由于计算顺序,等式右边两个值都已经得到了,这样就省去了枚举硬币个数的循环,时间复杂度降到 O(KV)
而此时, Fk(v) 的值还没有动过,如果省去下标 k ,用一维数组存储结果,即
F(v)=F(v)+F(vcoin[k])

相当于每次更新 F(v) 的值,就是用 k 个硬币的方案数将原来k1个硬币的方案数覆盖掉,不影响结果,空间复杂度也降到 O(V)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX 30100

int coin[5] = {1, 5, 10, 25, 50};
long long dp[MAX];

void solve(int total_sum){
    int i;
    for(i = 1; i < 5; i++){
        for(int sum = coin[i]; sum <= total_sum; sum++){
            dp[sum] += dp[sum - coin[i]];
        }
    }
}

int main(){
    memset(dp, 0, sizeof(dp));
    for(int sum = 0; sum <= MAX; sum++)
        dp[sum] = 1;
    int total_sum;
    solve(MAX);
    long long ans;
    while(scanf("%d", &total_sum) != EOF){
        ans = dp[total_sum];
        if(ans == 1)
            printf("There is only 1 way to produce %d cents change.\n", total_sum);
        else
            printf("There are %lld ways to produce %d cents change.\n", ans, total_sum);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值