算法-搞懂动态规划

30分钟弄懂动态规划算法详细讲解(超详细)

动态规划对于很多人来说是一道过不去的坎,因为很多的教程或者书籍都讲得太抽象,读者看了都云里雾里

其实动态规划是很简单的,今天,我就来讲讲动态规划是怎么实现的.

一 动态规划作用:

动态规划一般是来解决

1计数

2求最大值,最小值

3求存在性

img

二 动态规划怎么用(四部曲):

1.确定状态(两个核心:1最后一步 2化成子问题)

2转移方程

3开始和边界条件

4计算顺序

这么一说,太抽象了,这四部曲是什么鬼东西(黑人问号.jpg)???接下来先用例子分析分析

问题:(lintcode:669)

img

首先,看到这种题,先不考虑算法,直接凭直觉的想法是这样的

7元*3+5元*1+2元*1=28 (呃,好像不能这样),再这样

7元*3+2元*3=27 ,一共6枚硬币(这样总可以了吧)

其实,正确答案是7元*1+5元*4=27 ,5枚硬币(震惊!!!)

接下来用动态规划来(四部曲)分析:

1.确定状态

1.1"最后一步"

首先我们把最后一个硬币的面值是ak,那么前面的硬币值肯定是27-akimg

接下下,"最后一步"还有两个注意的关键点

img

1.2 化解子问题

img

还有个问题,我们还不知ak是多少,所以需要

img

这样我们的第一步就完成了

2 确定转移方程

根据第一步的状态,我们这样设置,(注意,上面的大括号在这一步已经变成方括号了)

img

完成这一步可以先喝杯咖啡了

3.确定开始和边界条件

首先我们的硬币肯定从f[0]即凑出0元需要0枚硬币,

接下来是边界问题:

img

4计算顺序

有了我们前面分析的前三步,接下来就是计算顺序了,其实就是从f[0]开始计算而已,并把他们都记录下来

img

结果其实是这样的

img

小结:

img

说了这么多,没代码啊,无代码无真相!!接下来上代码,

/**
 * 
 * @param {Array} coin 代表硬币的种类 [2,5,7]
 * @param {int} M 代表拼凑的值  27
 */
function coinChange(coin, M) {
    let f = []
    f[0] = 0 //初始化第一步
    for (let i = 1; i <= M; i++) {
        f[i] = Number.MAX_VALUE //开始默认无穷大,即不能拼凑

        for (let j = 0; j < coin.length; j++) {  //这里右三种面值的硬币,需要循环一次
            if (i - coin[j] >= 0 && f[i - coin[j]] != Number.MAX_VALUE) {//小于0即不能拼凑,和无穷大+1会报错
                f[i] = Math.min(f[i], f[i - coin[j]] + 1)
            }
        }
    }
    if (f[M] == Number.MAX_VALUE) {
        return -1
    }
    return f[M] //数组最后一个值就是我们想要的结果
}

接下来,继续看计数型的动态规划(lintcode:114)

img

解答这个问题,还是四部曲:

1.确定状态

1.1 “最后一步”

img

1.2化成子问题

img

2. 确定转移方程

img

3.确定开始和边界条件

img

4计算顺序

img

分析完了,上代码

/**
 * 
 * @param {int} m m行
 * @param {int} n n列
 */
/**
 * @param m: positive integer (1 <= m <= 100)
 * @param n: positive integer (1 <= n <= 100)
 * @return: An integer
 */
const uniquePaths = function (m, n) {
    let f=[];//先声明一个一维数组的
    for(let i=0;i<m;++i){
        f[i]=[];//声明二维数组的,其他语言有其他的写法
        for(let j=0;j<n;++j){
            if(i===0||j===0){
                f[i][j]=1;
            }else{
                f[i][j]=f[i-1][j]+f[i][j-1];
            }
        }
    }
    console.log(f)
    return f[m-1][n-1];
};

接下来继续分析一道题(lintcode:116):

img

1.确定状态

1.1"最后一步"

img

1.2 化为子问题

img

2. 确定转移方程

img

3.确定开始和边界条件

img

4. 计算顺序

img

最后,上代码

/**
 * 
 * @param {Array} a //代表每个石头能跳的长度 
 */
function canJump(a){
    let n=a.length //数组的长度为能跳的总石头数
    let f=[]
    f[0]=true
    for(let j=1;j<n;j++){
        f[j]=false
        for(let i=0;i<j;i++){
            if(f[i]&&a[i]+i>=j){
                f[j]=true
                break
            }
        }
    }
    return f[n-1]
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值