动态规划求解最多有几种方案求解硬币找零问题
一,问题描述
假设有 m 种面值不同的硬币,存储在 coinsValues数组中,现需要使用这些硬币来找钱,各种硬币的使用个数不限。 求对于给定的钱数N,我们最多有几种不同的找钱方式。硬币的顺序并不重要。
二,动态规划分析
为了更好的分析,先对该问题进行具体的定义:将用来找零的硬币的面值存储在一个数组中。如下:
coinsValues[i] 表示第 i 枚硬币的面值。比如,
第 i 枚硬币 面值
1 1
2 3
3 4
待找零的钱数为 n (上面示例中 n=6)
为了使问题总有解,一般第1枚硬币的面值为1
设 c[i,j]表示 使用 第 1,2,...i 种面值的硬币时,需要找金额为 j 的钱,最多可采用多少种不同的方式?
i 表示可用的硬币种类数, j 表示 需要找回的零钱
①最优子结构
对于某种面值的硬币,要么使用了(可能使用多次)它,要么不使用它。故:
c[i,j]=c[i-1,j] + c[i,j-coinsValue[i]]
c[i-1,j] 表示不使用第 i 枚硬币, c[i, j-coinsValue[i]] 表示至少使用了一次 第 i 枚硬币。c[i, j-coinsValue[i]] 表示,第 i 枚硬币还可以继续使用。因为第一个参数还是 i
从这里可以看出:用到了《组合数学》中的加法原理。
如何确定初始(基准)条件?一个重要的方法就是画一个简单的实例图。(借用网上一张图:)
C({1,2,3},j) --> recursiveChargeTypes
C({1,2,3}, 5)
/ \
/ \
C({1,2,3}, 2) C({1,2}, 5)
/ \ / \
/ \ / \
C({1,2,3}, -1) C({1,2}, 2) C({1,2}, 3) C({1}, 5)
/ \ / \ / \
/ \ / \ / \
C({1,2},0) C({1},2) C({1,2},1) C({1},3) C({1}, 4) C({}, 5)
/ \ / \ / \ / \
/ \ / \ / \ / \
. . . . . . C({1}, 3) C({}, 4)
/ \
/ \
. .
比如,按照红色那条路走,就知道 5 使用了硬币面值3 和 2,故成功找零,此时 j=0了,这是一种找零方式 ==》 当j==0时,返回1
三,代码实现
public class DPCoinCharge { public static int chargeTypes(int[] coinsValues, int n){ int m = coinsValues.length; int[][] c = new int[m+1][n+1]; //基准条件,可参考下面的递归代码 for(int i = 0; i <=m; i++) c[i][0] = 1; for(int i = 1; i <=n; i++) c[0][i] = 0; for(int i = 1; i <=m; i++) { for(int j = 1; j <=n; j++) { if(j < coinsValues[i-1])//第 i 枚硬币 不可用. (需要找 5块钱,但是现在只有一张百元大钞) { c[i][j] = c[i-1][j]; continue; } //在第 i 枚硬币可用的情况下, 不使用 第 i 枚硬币 或者第 i 枚硬币至少使用一次---状态方程 c[i][j] = c[i-1][j] + c[i][j - coinsValues[i-1]];//coinsValues下标从0开始 } } return c[m][n]; } //递归实现 public static int recursiveChargeTypes(int[] coinsValues, int m, int n) { //基准条件 可以 通过画一个简单的实例 分析来得出. 比如 recursiveChargeTypes({1,3,4}, 3, 5) if(n == 0) return 1; if(n < 0) return 0; if(m <= 0) return 0; else return recursiveChargeTypes(coinsValues, m-1, n) + recursiveChargeTypes(coinsValues, m, n-coinsValues[m]); } public static void main(String[] args) { int[] coinsValues = {1,2,3}; int n = 5; int maxTypes = chargeTypes(coinsValues, n); System.out.println(maxTypes); } }
四,参考资料
http://www.acmerblog.com/dp6-coin-change-4973.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 零经验选手,Compose 一天开发一款小游戏!
· 通过 API 将Deepseek响应流式内容输出到前端
2015-06-13 词典的实现(1)--Map的底层实现