将一面额为10元倍数的整钱(面额为大于等于50,并小于等于100)换成1元、2元和5元的零钱组合(每种面值都要有)。输入要换的面额(如10元),输出所有可能的换法数量(如2种)。

递归

  1. 定义递归函数为dfs(int a, int x, int y, int z),a代表当前面值,x,y,z 分别对应1块,2块,3块的数量。
  2. 先找到递归函数出口,如下图所示,当a的值为负值时,不必递归下去,以及当a等于0时,表示已经找换完钱了,所以也可以停止递归下去。
  3. 接着找递归体,如下图所示,有三个分支,分别是a-1, a-2, a-5,将原问题对a的求解,递归为a-1, a-2, a-5三个子问题的求解,三个子问题又会通过递归求解。
    请添加图片描述
    代码如下:
#include <stdio.h>

int count = 0;
int dp[100][100][100];

void dfs(int a, int x, int y, int z){
    if(a < 0) return;
    if(a == 0 && dp[x][y][z] != 1) {
        dp[x][y][z] = 1;
        count++;
        return;
    }
    printf("(%d,%d,%d,%d)\n",a,x,y,z);
    dfs(a-1, x+1, y, z);
    dfs(a-2, x, y+1, z);
    dfs(a-5, x, y, z+1);
}

int main()
{  
    int price;
    printf("请输入面额: ");
    scanf("%d",&price);
    dfs(price-8,1,1,1);
    printf("the count is %d",count);
    return 0;
}
  1. 变量count表示换法的数量
  2. 三维数组dp[x][y][z]用来表示换钱方案的状态,x,y,z 分别对应1块,2块,3块的数量,比如dp[1][1][1]表示一张1块,一张2块,一张3块。由于数组是在全局定义的,因此数组里面存放的都是0, 值为0的时候表示(x,y,z)这种方案没有被采取,值为1表示采取该方案,count值加一。举个例子:10块钱有两种换法,一是(3,1,1),二是(1,2,1),因此在递归的过程中会对数组dp[3][1][1],dp[1][2][1]都赋值1,表示采取这种方案,这么做的目的是避免采取重复的方案
  3. 在main函数中调用dfs方法,由于题目要求每种面值都要有,所以我这里就直接传入参数(price-8, 1, 1, 1)

但是由于重复的路径过多,面值达到 34 左右时间就爆了,因此需要剪枝 \textcolor{red}{但是由于重复的路径过多,面值达到34左右时间就爆了,因此需要剪枝} 但是由于重复的路径过多,面值达到34左右时间就爆了,因此需要剪枝

剪枝

实际上就是在递归调用前,在dp数组中记录当前的状态,由于当前的子问题在其他路径可能会重复,相同的子问题的解决方案都是相同的(都是通过递归三条分支寻找结果,父问题相同,递归三条分支形成的三个子问题也是相同的),所以当其他路径递归时,遇到了之前已经遇到过的问题,直接return就行,不需要再递归了,可以减少很多重复的路径

#include <stdio.h>

int count = 0;
int dp[100][100][100];

void dfs(int a, int x, int y, int z){
    if(a < 0) return;
    if(dp[x][y][z] == 1) return; //以前遇到过的情况直接回退
    if(a == 0 && dp[x][y][z] != 1) {
        dp[x][y][z] = 1;
        count++;
        return;
    }
    printf("(%d,%d,%d,%d)\n",a,x,y,z);
    int index = 1;
    while(index <= 3){
    	//在递归调用前,在dp数组中记录当前的状态
        if(index == 1) { dp[x][y][z] = 1; dfs(a-1, x+1, y, z);}
        else if(index == 2) { dp[x][y][z] = 1; dfs(a-2, x, y+1, z);}
        else if(index == 3) { dp[x][y][z] = 1; dfs(a-5, x, y, z+1);}
        index++;
    }
}

int main()
{  
    int price;
    printf("请输入面额: ");
    scanf("%d",&price);
    dfs(price-8,1,1,1);
    printf("the count is %d",count);
    return 0;
}

while循环那里水平有限,只能写成那样了

两层for循环

实际上这道题两层for循环就能搞定,以下是链接
for循环解法

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值