30分钟弄懂动态规划算法详细讲解(超详细) - Go语言中文社区

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


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

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

一 动态规划作用:

动态规划一般是来解决

1计数

2求最大值,最小值

3求存在性

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

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

2转移方程

3开始和边界条件

4计算顺序

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

问题:(lintcode:669)

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

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

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

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

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

1.确定状态

   1.1"最后一步"

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

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

  1.2 化解子问题

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

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

2 确定转移方程

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

 

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

3.确定开始和边界条件

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

接下来是边界问题:

 

4计算顺序

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

结果其实是这样的

 

小结:

 

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

 

/**
 * 
 * @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)

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

1.确定状态

     1.1 "最后一步"

  1.2化成子问题

2.  确定转移方程

 

3.确定开始和边界条件

4计算顺序

分析完了,上代码

 

/**
 * 
 * @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):

1.确定状态

   1.1"最后一步"

   1.2 化为子问题

2. 确定转移方程

3.确定开始和边界条件

4. 计算顺序

最后,上代码

/**
 * 
 * @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]
}

现在,动态规划基本完成入门了,都是四部曲.

版权声明:本文来源CSDN,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/sinat_19594515/article/details/102738781
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。
  • 发表于 2021-04-08 10:02:46
  • 阅读 ( 1316 )
  • 分类:算法

0 条评论

请先 登录 后评论

官方社群

GO教程

猜你喜欢