【编程马拉松】【023-换零钱】
2016-07-07 11:22
281 查看
【编程马拉松算法目录】
【023-换零钱】【工程下载>>>】
1 题目描述
考虑仅用1分、5分、10分、25分和50分这5种硬币支付某一个给定的金额。例如需要支付11分钱,有一个1分和一个10分、一个1分和一个5分、六个1分和一个5分、十一个1分这4种方式。请写一个程序,计算一个给定的金额有几种支付方式。注:假定支付0元有1种方式。1.1 输入描述:
输入包含多组数据。每组数据包含一个正整数n(1≤n≤10000),即需要支付的金额。1.2 输出描述:
对应每一组数据,输出一个正整数,表示替换方式的种数。1.3 输入例子:
11 26
1.4 输出例子:
4 13
2 解题思路
假设硬币的种类数组t={1,5,10,25,50},按大小排序。m表示选择有0~m种硬币可以选择,面值是t[0]、•••、t[m-1]。要换的钱的数目是n。本题可以使用动态规划算法解决。2.1 递归方式
假设有n钱待找零,当前可以供选择的方式为m种,f(n,m)表示共的待找零方案,则有递推公式:f(n,m)=⎧ ⎩ ⎨ 10f(n−t[m−1],m)+f(n,m−1) n=0n<0orm≤0n>0andm>0
当n=0,f(n,m)=1,表示已经找零完毕,再找0元只有一种方案。
当n<0表示这种方案找零不合理,不能完成找零操作,而m≤0说明找零还没有完成,但是已经没有可以供选择的硬币了。所以f(n,m)=0。
对于可以找零,并且还有硬币选择的情况找零有两种方案。第一种是:选择一个可以选择的最大面值的,剩下的钱再进行找零操作,同时硬币种类的选择方案没有变化,即为:f(n-t[m-1],m)。第二种是:现在和以后都不选择本次可以选择的最大的硬币面值,然后再进行找零操作。即f(n,m-1)。
2.2 非递归方式
假设有n钱待找零,当前可以供选择的方式为m种,创建一个长度为n+1的数组r,r[i]表示找零为i的找零方法为r[i]。初始时r的第一个元素为1,其它元素都为0,即r[0]=1,r[i≠0]=0。步骤一、因为硬币的面值都按大小排序,从最小的面值开始选择,先选择最小的一个t[0]。对于找零为大于0的情况只有从t[0]开始才可能有找零的情况。对于i≥t[0]有r[i]=r[i]+r[i-t[0]]。这是只有一种硬币可以选择的情况。
步骤二、当有2种硬币可以选择,在步骤一已经求出了只有一种硬币可供选择的情况,现在可以选择第二种硬币,那么只有找零数i≥t[0]时才可以选择第二种硬币,所以有r[i]=r[i]+r[i-t[1]]。
同理可以求得有3、4、•••。具体实现详见代码。
3 算法实现
import java.util.Scanner; /** * Author: 王俊超 * Time: 2016-05-13 15:15 * CSDN: http://blog.csdn.net/derrantcm * Github: https://github.com/Wang-Jun-Chao * Declaration: All Rights Reserved !!! */ public class Main { // 硬币可以供选择的面值 private final static int[] T = {1, 5, 10, 25, 50}; public static void main(String[] args) { Scanner scanner = new Scanner(System.in); // Scanner scanner = new Scanner(Main.class.getClassLoader().getResourceAsStream("data.txt")); while (scanner.hasNext()) { int n = scanner.nextInt(); // System.out.println(exchange(n, T.length)); System.out.println(exchange(n)); } scanner.close(); } /** * 解法一:递归解法 * 找零操作 * * @param n 当前要找的零钱数 * @param m 可以选择的硬币种类T[0]~T[m-1] * @return 不的找零数目 */ private static int exchange(int n, int m) { if (n == 0) { return 1; } else if (n < 0 || m <= 0) { return 0; } else { return exchange(n - T[m - 1], m) + exchange(n, m - 1); } } /** * 解法二:非递归解法 * 找零操作 * * @param n 当前要找的零钱数 * @return 不的找零数目 */ private static long exchange(int n) { // 选择long不然可能会超出int表达范围 long[] result = new long[n + 1]; // 初始化 result[0] = 1; // 每次增加一种硬币,且比之前硬币的面值大,计算加入新的硬币后每个数目的钱其找零数有多少种 for (int t : T) { for (int j = t; j <= n; j++) { result[j] += result[j - t]; } } return result ; } }
4 测试结果
5 其它信息
因为markddow不好编辑,因此将文档的图片上传以供阅读。Pdf和Word文档可以在Github上进行【下载>>>】。相关文章推荐
- 如何学好C++
- Qt creator创建纯C/C++源代码
- java nio(non-blocking io)简介及和io
- Qt之QFileSystemWatcher
- myBatis + SpringMVC上传、下载文件、显示byte图片文件、等比例缩放图片
- 什么是预编译?何时需要预编译?
- Qt之QFileSystemWatcher
- Qt之QFileSystemWatcher
- lua 中的点、冒号与self
- 由浅到深讲解C#-LINQ
- java class路径
- C# 6.0的属性(Property)的语法与初始值详解
- Python新手学习基础之初识python——与众不同2
- php版网易视频云api
- STM32F103 GU906B模块GPRS、短信收发、拨号等功能的实现
- SPRING BOOT 配置大全
- C# 读取xls格式的文件
- eclipse生成boolean型变量的getter是is开头
- java list倒序输出及复制list集合
- InputStreamReader/OutputStreamWriter乱码问题解决