从数硬币来比较贪心算法和动态规划

写在前面

其实,在刚接触编程时候,对于贪心算法和动态规划的区别一直感觉比较模糊,今天就数硬币这道题,来和大家认识一下贪心算法和动态规划。

题目如下:

  • 我们一共需要找money块钱
  • 我们有一些硬币,币值在数组中,比如[1,3,4]* 表示我们一共有3个硬币,币值分别为1,3,4
  • 问题:如何找钱,能使用最少的硬币数?

贪心算法的分析

拿到这个问题,我们首先就会去想,大币值能顶几个小币值,那么我们尽量去取大的币值,不就能够取到最少的硬币数了吗?

  • 这种尽量去拿大的,拿不下了再拿次大的
  • 这种算法就是贪心算法
  • 我们可以尝试来写一下这个算法* 我们会发现,这个算法并不能算出最佳值* 以找6块,[1,3,4]硬币为例
let money = 6;
let corn = [1, 3, 4];
function greedy(money, corn) {let cnt = 0;for (let i = corn.length - 1; i >= 0; i--) {while (money >= corn[i]) {money -= corn[i];cnt++;}}return cnt;
}

let ans = greedy(money, corn);
console.log("贪心算法", ans); 
  • 我们可以口算,也可以看程序的输入
  • ans=3* 显然这不是最佳答案* 我们先拿了4,然后3拿不下,然后连着拿两个1,一共是3个硬币* 但是,我们知道正确答案其实是2个硬币,2个3
  • 所以来说,贪心算法并不能完成所有的需求* 也有一些人生的哲理吧* 也许偶尔的弯路能让你走得更远* 也类似机器学习,学到某个高峰后就会停滞,所以到不了最高峰

动态规划的分析

  • 我们知道从整体每步都取比较有利的一步,最后可能拿不到最佳的效果
  • 但是动态规划是从每一小点取最佳的一步,这就是贪心和动态规划最大的区别
  • 动态规划最重要的就是找到状态转移方程* 那我们先申请一个数组let dp = new Array(money + 1).fill(Infinity);* 这个数组用来存放某个额值所需要的硬币数* 我们初始化是正无穷,是方便后面通过动态转移方案来更新dp数组* 下面我们来分析一下状态转移方程* dp[i]可以看成我在某个额值上取一个硬币,也就是dp[i-coin[j]]+1* 这个地方是取最小值哦!min,因为我们要找到最少的硬币数* 然后我们同时去遍历所有的硬币,就可以拿到dp[i]的最佳值* 因为i-coin[j]<i,所以dp[i-coin[j]]也是最佳值* 所以在最佳值上+1,dp[i]就也是最佳值了* 总上,我们的状态转移方程就是dp[i] = min(dp[i], dp[i - coin[j]] + 1);* 当然,我们要判断i - coin[j] >= 0,来保证dp[i]是存在的
let money = 6;
let coin = [1, 3, 4];
function min(a, b) {return a < b ? a : b;
}
function dynamic(money, coin) {let cnt = 0;let dp = new Array(money + 1).fill(Infinity);for (let i = 0; i < coin.length; i++) {dp[coin[i]] = 1;}for (let i = coin[0]; i <= money; i++) {for (let j = 0; j < coin.length; j++) {if (i - coin[j] >= 0) dp[i] = min(dp[i], dp[i - coin[j]] + 1);}}return dp[money];
}

let ans = dynamic(money, coin);
console.log("动态规划", ans); 
  • 我们来模拟运行一下,为什么动态规划的结果是2* 在i=6的时候* dp[i]=Infinity* 循环j,分别是* j=0,dp[6-1]+1=>dp[5]+1=>…=>2+1=3* j=1,dp[6-3]+1=>dp[3]+1=>…=>1+1=2* j=2,dp[6-4]+1=>dp[2]+1=>…=>2+1=3
  • 这样模拟了一下应该会更加清晰了

最后再贴一下对应的TypeScript代码吧!

贪心算法

let money:number = 6;
let coin:number[] = [1, 3, 4];
function greedy(money:number, coin:number[]) {let cnt:number = 0;for (let i = coin.length - 1; i >= 0; i--) {while (money >= coin[i]) {money -= coin[i];cnt++;}}return cnt;
}
let ans = greedy(money, coin);

console.log('贪心算法',ans); 

动态规划

let money:number = 6;
let coin:number[] = [1, 3, 4];
function min(a:number, b:number) {return a < b ? a : b;
}
function dynamic(money:number, coin:number[]) {let cnt = 0;let dp = new Array(money + 1).fill(Infinity);for (let i = 0; i < coin.length; i++) {dp[coin[i]] = 1;}for (let i = coin[0]; i <= money; i++) {for (let j = 0; j < coin.length; j++) {if (i - coin[j] >= 0) dp[i] = min(dp[i], dp[i - coin[j]] + 1);}}return dp[money];
}

let ans = dynamic(money, coin);
console.log("动态规划", ans); 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值