欧拉计划31题

Coin sums
In England the currency is made up of pound, £, and pence, p, and there are eight coins in general circulation:
1p, 2p, 5p, 10p, 20p, 50p, £1 (100p) and £2 (200p).
It is possible to make £2 in the following way:
1×£1 + 1×50p + 2×20p + 1×5p + 1×2p + 3×1p
How many different ways can £2 be made using any number of coins?

 题目:
在英国,货币是由英镑 £,便士 p 构成的。一共有八种钱币在流通:
1p, 2p, 5p, 10p, 20p, 50p, £1 (100p) 和 £2 (200p).
要构造 £2 可以用如下方法:
1×£1 + 1×50p + 2×20p + 1×5p + 1×2p + 3×1p
允许使用任意数目的钱币,一共有多少种构造 £2 的方法?

        这个题目咋一看,一点思路都没有;这里 £2直接用200元说了,上面也一样转化为这样;那么先通过去判断,构成多少钱方法的总数受什么影响,受钱币的种类和需要构成多少钱;

        比如凑5元有两个方案,一个方案里有1元和2元,一个方案里只有一元,那么这两个方案的构成方法是不同的第一个方案有3种分别是5个一元,3个1元和1个2元,1个1元和2个2元 (题目没有要求每种货币必须用),然后第二个方案只有一种,5个一元;那么货币的种类确实影响构成多少钱的方法总数;

        然后比如手里有1元和2元,去凑5元和10元的方法总数也是不同的,这里就不去举例子了,可以非常简单的想到种类是不同的;

        那么这就怎么了,这就确定了递推状态,假如钱币的种类为n,需要凑多少钱为m,那么可以写出公式f(n,m) = S,S就是用n种钱币筹m的方法总数;

        确定递推状态后,那么就开始推导递推公式,利用递推状态推到出的公式进行去推到;n是钱币种类,m是需要凑多少钱;只有这一个公式肯定是求不出最后个结果的,而递推过程就是把一个大的问题去拆分为多个小的问题,然后通过小的问题结果来结合起来得到最终的大的问题的结果,这就是对于递推的一个简单的解释;现在问题又转化了,如何利用上面的公式去转化为可以求道小的问题的公式;下面来看:

        f((n-1),m)这是求n-1种钱币来凑m元的钱币的方法总数

        f(n, (m - money[n]))money[n] 代表的是第n种钱币的金额,这里表示的是用n种钱币去凑m减去了第n种钱币的金额的方法总数;

        这两个公式的和可以等于:

        f(n,m) = f(n-1, m) + f(n, m - money[n]));

        还是用举个例子的方法

        1,2元凑5元

        f(2, 5) = f(1,5) + f(2, 5 - money[2])\\ f(2, 5) = 1 + f(2, 3)\\ f(2, 5) = 1 + f(1, 3) + f(2, 1)\\ f(2,5) = 1 + 1 + 1 = 3

        这就是一个简单的递推过程演示;那么有了递推公式就可以开始写代码了,接下来就是代码实现:

#include <stdio.h>
#define MAX_M 8
#define MAX_N 200
int money[MAX_M + 5] = {1, 2, 5, 10, 20, 50, 100, 200};
int f[MAX_M + 5][MAX_N + 5];

int main() {
    for (int i = 0; i < MAX_M; i++) {
        f[i][0] = 1;//为什么等于1,就比如用一元凑一元只有一种方法
        for (int j = 1; j <= MAX_N; j++) {
            if (i > 0) f[i][j] += f[i - 1][j];//if语句防止非法访问
            if (j >= money[i]) f[i][j] += f[i][j - money[i]];
        }
    } 
    printf("%d\n", f[7][200]);
    return 0;
}

         第二种写法滚动数组:

#include <stdio.h>
#define MAX_M 8
#define MAX_N 200
int money[MAX_M + 5] = {1, 2, 5, 10, 20, 50, 100, 200};
int f[2][MAX_N + 5];

int main() {
    for (int i = 0; i < MAX_M; i++) {
        int k = i % 2;
        f[k][0] = 1;
        for (int j = 1; j <= MAX_N; j++) {
            if (i > 0) f[k][j] = f[k ^ 1][j];//因为k为1或0,k本来是0异或后就是1
            if (j >= money[i]) f[k][j] += f[k][j - money[i]];
        }
    } 
    printf("%d\n", f[(8 - 1) % 2][200]);
    return 0;
}

         第三种写法一维数组,因为每次计算只会用到上一次的种类的方法总数,例如

        f(7,200) = f(6,200) + f(7, 200 - money[7]);

        就只会用到f(6,200) 而 f(7,200 - money[7])在这次循环种计算过了,下面的图可以帮助你理解:

        

        接下来看代码如何实现:

#include <stdio.h>
#define MAX_M 8
#define MAX_N 200
int money[MAX_M + 5] = {1, 2, 5, 10, 20, 50, 100, 200};
int f[MAX_N + 5];

int main() {
    f[0] = 1;//还是刚才的例子,1元筹1元种的类为1,200筹200也是一种
    //也就是f[200 - 200] = 1;这个意思
    for (int i = 0; i < MAX_M; i++) {
        for (int j = 1; j <= MAX_N; j++) {
            if (j >= money[i]) f[j] += f[j - money[i]];
        }
    } 
    printf("%d\n", f[200]);
    return 0;
}

最终答案:73682 

         通过二维数组压缩,在通过压缩后的二维变一维,这就是如何节省空间的过程,实现代码,不仅仅只是实现,还有考虑运行时间,占用内存空间,这才是写代码;就比如一个优秀的游戏他的运行速度快,占用内存空间小,一样的道理;

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

初猿°

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值