【递归算法题】硬币表示

简介: 【递归算法题】硬币表示
/*
 * 假设我们有8种不同面值的硬币{1,2,5,10,20,50,100,200},
 * 用这些硬币组合够成一个给定的数值n。例如n=200,那么一种可能的组合
 * 方式为 200 = 3 * 1 + 1*2 + 1*5 + 2*20 + 1 * 50 + 1 * 100. 
 *  问总共有多少种可能的组合方式?
 *  2、华为面试题
 *  1分2分5分三种硬币,组成已一角/共有多少解法
 *  3.cc150 给出 1 5 10 25  有多少种方法
 *  
 *  思路: 1、输入总价,返回一个函数包含(总价,数组,最大的下标)
 *        2、判断下标是否等于0 则返回1
 *        3、定义有有多少种解法 进行一个for循环
 */

此问题简单的来想,可以有明确的递推关系。我们可以选择用递归来做。(首先讲递推,递归在后面)

思路:
假定现已给定四种面值,给定n。

我们可以对于每种面值的硬币,从大到小,选择要或者不要。

如果要剩下多少,如果不要剩下多少,将剩下的钱进行下一次递归,同时换一种面值的硬币。

这样就可以算出各种情况下要凑齐n所需的解法。

  • 如果要凑的n为0,那么无论用那种面值,都是一种解法
  • 如果用的面值为1,无论n为多少,都是一种凑法
  • 当n小于5时,无论用多少种面值(1必须要),都是一种凑法

下图为每种n对应的用不同硬币的凑法,四行从上到下对应1,5,10,25面值,每一列为从1到n。具体的解释都在代码注释里。

#include<stdio.h>
#include<string.h>
//#include<iostream>
//using namespace std;
const int coins[4] = {1, 5, 10, 25};
int main(){
  int n;
  scanf("%d",&n);
  const int N = n;
  int a[4][N+1];
  memset(a,0,sizeof(a));
  for(int i = 0; i < 4; i++)
    a[i][0] = 1;                             //面值为0的凑出来的部分,初始化为1
  for(int i = 0; i <= n; i++)
    a[0][i] = 1;                             //用面值为1凑出来的都初始化为1
  for(int i = 1; i < 4; i++){                  // 硬币的种类 
    for(int j = 1; j <= n; j++){             // 总金额
      for(int k = 0; k*coins[i] <= j; k++){// 在选择k个当前最大面值的硬币情况下的种类数 
        a[i][j] += a[i-1][j-k*coins[i]]; // x+y+z+1(在选择了k个当前面值的情况下,剩余的总金额的解法)
      }
    }
  }
//  for(int i = 0; i < 4; i++){
//    for(int j = 0; j <= n; j++){
//      cout << a[i][j] << " ";
//    }
//    cout << endl;
//  } 
  printf("%d\n",a[3][n]); 
  return 0;
}

由这个思路,我们可以利用递归,减小因开辟二维数组带来的空间开销。

对于当前的状态,我们都可以转化为是否要当前面值、要几张的解法,加上是否要下一个面值……一直到最后。

#include<stdio.h>
const int coins[4] = {1, 5, 10, 25};
int countWaysCore(int n, int cur){             //n表示输入的要凑的数,cur为coins的下标,用于递归时选择当前面值
  //cur每次递归递减,出口条件为cur=0
  if(cur == 0) return 1;                     //如果cur=0,只有一种凑法
  int res = 0;                               //如果不是0,用res记录要返回的结果
  for(int i = 0; i*coins[cur] <= n; i++){    //此循环表示是否选当前面值的硬币,选几张。
    int left = n-i*coins[cur];             //left表示剩余的n,用于下一次递归
    res += countWaysCore(left,cur-1);    //res记录当前解法,递归完所有层,res迭代为总解法
  }
  return res;
}
int countWays(int n){
  if(n <= 0) return 0;
  return countWaysCore(n,3);
}
int main(){
  int n;
  while(~scanf("%d",&n)){
    printf("%d\n",countWays(n));
  }
  return 0;
}
相关文章
|
4月前
|
消息中间件 Kubernetes NoSQL
动态规划-状态压缩、树形DP问题总结
动态规划-状态压缩、树形DP问题总结
|
10月前
LeetCode题:70爬楼梯,126斐波那契数
LeetCode题:70爬楼梯,126斐波那契数
49 0
|
算法 Java Python
深入理解动态规划算法 | 凑硬币
深入理解动态规划算法 | 凑硬币
113 0
|
算法 Java
动态规划算法-凑硬币
动态规划算法-凑硬币
107 0
汉诺塔?爬楼梯?斐波那契?知道递归就够了
汉诺塔?爬楼梯?斐波那契?知道递归就够了
83 0
爬楼梯(动态规划)
假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
60 0
|
机器学习/深度学习 算法
斐波拉契数列的递推递归求解算法
斐波拉契数列的递推递归求解算法
100 0
|
算法
【动态规划法】硬币找零问题
【动态规划法】硬币找零问题
323 0
【动态规划法】硬币找零问题
|
机器学习/深度学习 人工智能 算法
『递归』汉诺塔和全排列
使用递归编写一个程序实现汉诺塔问题,要求在输入圆盘数量之后,输出圆盘的移动步骤,输出格式示例如下: 第1步:1号盘从A柱移至B柱第2步:2号盘从A柱移至C柱
214 0
|
人工智能
Letcode 53. 最大子序和 (动态规划+分治法求解)
Letcode 53. 最大子序和 (动态规划+分治法求解)
119 0