什么是动态规划??????

1:含义

  如果我们顾名思义的理解,就会产生很多误解!!!这里根本没有动态的思想,最本质的思想就是数列的思想,将上次的计算结果暂存起来,然后根据状态转移表达式,已经不断地后移动,解决原来复杂的问题。详细理解我们参看知乎这位同学的分析,很是到位。

如何理解动态规划? - zhen tan的回答 - 知乎 https://www.zhihu.com/question/39948290/answer/883302989 

  实话实话,我一开始也被这万恶的名字所困扰住了,以为这是一种很奇妙的想法,当前的确这就是一种很有用,实际的想法的,但是我们如何真实理解dp思想尼?最简单的理解就是1+1+1+1+1+1+1+1+1 = ?这个结果等于多少?我们算一会发现是9,那么如果再+1尼?我们可以很块知道结果为10。为什么尼?因为我们知道前面的结果,同时也知道前面结果和现在结构的关系,所以可以很轻松知道本次结果。用严谨的语言来说,就是我们知道上次的暂存结果,同时知道两次结果之间的状态转移方程,也即是两次结果的之间的关系?即如何通过上次结果求得本次结果。这两个条件是解决这类问题的关键。

2:三要素

  1. 状态转移方程。就是用来描述前后两次之间结果的关系的方程组,如何通过上次的结果,来推导出本次的结果,这也是dp中最重要的地方,如果这里要是做不出来,那肯定没办法了!!!
  2. 暂存上次结果。这一步状态转移方程的基础。只有再知道上次结果的基础之上,再配合状态转移方程才能轻松的到本次的结果。
  3. 控制循环方向。简单来说就是控制问题的解决方向,来使得问题是按照从上一个结果,到这个结果,再到下一个结果的顺序开展的。

3:案例分析

3.1:斐波那契数列

3.1.1:问题描述

  指的是这样一个数列:1、1、2、3、5、8、13、21、34、……在数学上,斐波那契数列以如下被以递推的方法定义:F(1)=1,F(2)=1, F(n)=F(n - 1)+F(n - 2)(≥ 3,∈ N*)

3.1.2:问题分析

  如何按照动态规划的思想来理解和分析问题尼?

  1. 状态转移方程:在这个题目中,本次结果和上次结果之间的关系是很清楚的,正如题中说明的,F(n)=F(n - 1)+F(n - 2)。本次结果需要上次和上上次结果之和,这就是状态转移方程。
  2. 暂存上次结果:该怎么按照我们状态转移方程的要求去保存和提供之前的结果尼?
  3. 控制前进方向:

3.1.3:代码示例

package DP;

/**
 * @author :dazhu
 * @date :Created in 2020/3/28 15:06
 * @description:动态规划的学习
 * @modified By:
 * @version: $
 */
public class Main {
    public static void main(String[]args){
        Fibo fibo = new Fibo();
        for(int j=0;j<20;j++)
        System.out.println(fibo.getFibo(j));
    }
}

class Fibo {
    //如果按照DP的思想去解决尼?
    //状态转移方程,F(n) = F(n-1) + F(n-2);
    //暂存上次结果,由于状态转移方程每次需要上次和上上次的结果,所以我们定义两个变量用来存放上次和上上次的结果
    //控制求解方向,从小到大的来计算。
    public long getFibo(int n){
        //暂存上次结果
        long lastResult = 1;//fibo(1) = 1
        long lastLastResult = 0;//fibo(0) = 0
        long thisResult = 0;
        if(n<2){
            return n;
        }
        //控制前进方向
        for(long i=2;i<=n;i++){
            //状态转移方程
            thisResult = lastResult + lastLastResult;
            //暂存上次结果
            lastLastResult = lastResult;
            lastResult = thisResult;
        }
        return thisResult;
    }
}

上面就是严格按照DP的思想来解决的问题。

3.2:不同路径

3.2.1:问题描述

一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。

机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。

问总共有多少条不同的路径?

 

例如,上图是一个7 x 3 的网格。有多少可能的路径?

示例 1:

输入: m = 3, n = 2
输出: 3
解释:
从左上角开始,总共有 3 条路径可以到达右下角。
1. 向右 -> 向右 -> 向下
2. 向右 -> 向下 -> 向右
3. 向下 -> 向右 -> 向右
示例 2:

输入: m = 7, n = 3
输出: 28
 

提示:

1 <= m, n <= 100
题目数据保证答案小于等于 2 * 10 ^ 9

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/unique-paths
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

3.2.2:问题分析

  从dp的思考角度去尝试解决这个问题?首先定性这个问题的本质,是不是一个dp的问题即能不能通过前面的结果得到本次结果,有没有状态转移方程?符不符合dp问题特征,当然有些问题问题只能大概分析出来基本特征,具体能不能dp我们还不是说一开始就能解决你的。按照dp问题的三要素去分析问题如下:

  1. 状态转移方程,很显然经过我们的分析发现,这里有明确的状态转移方程体现。
    N(i,j) = N(i-1,j) + N(i,j-1);(边界位置除外)
  2. 暂存之前结果,由于为了得到本次的路径树,我们至少组要左和上个点的路径数,依次类体,我们不得不通过建立一个等大的二维数组来存放每个格子的路径数
  3. 控制问题解决方向,很显然要想的到结果,我们不计算出所有的二维数组上格子的路径数才行,我们按照从上到下,从左到右的方式来解决这个问题。

3.2.3:代码示例

package LeetCode62;

/**
 * @author :dazhu
 * @date :Created in 2020/3/28 15:39
 * @description:不同路径
 * @modified By:
 * @version: $
 */
public class Main {
    public static void main(String[] args) {
        Solution solution = new Solution();
        System.out.println(solution.uniquePaths(7,3));
    }


}

//如何从dp的角度去解决问题?这才是我的目的啊
//算法思路:经过分析题目我们发现,到达右下角的路径个数等于到达其上面格子和左面各种路径个数之和。
//有状态转移公式:N(i,j) = N(i-1,j) + N(i,j-1);(边界位置除外)
//有了状态转移方程,就要考虑如何去暂存上次结果来为本次计算提供基础尼?首先我们发现这个
//问题里面,不是简单的一个两个暂存的问题,而是只有通过暂存每一个节点的结果才能得到后面的结果。
//所以这里设计一个等大的二维数据来暂存到达该位置的路径数
class Solution {
    public int uniquePaths(int m, int n) {
        //n:行数
        //m:列数
        //暂存结果的二维数组
        int[][] arrayResult = new int[n][m];
        //起始位置的路径个数应该置1代表这个位置路径为1
        arrayResult[0][0] = 1;

        //解决问题的方向,设计两个循环分别自上向下和自左向右的进行计算得到,每一点的路径结果
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                //判断是不是边界位置,如果是边界位置则只有一个各格子能到达这里。
                if (i == 0)//第0行,边界位置,则只有左边点过来的一条路径,所以结果不变
                    arrayResult[i][j] = 1;
                else {
                    if (j == 0)//第0列,边界位置,则只有上边点过来的一条路径,所以结果不变
                        arrayResult[i][j] = 1;
                    else {
                        //不为边界的格子才是左和上的路径之和
                        arrayResult[i][j] = arrayResult[i - 1][j] + arrayResult[i][j - 1];
                    }
                }
            }
        }
        return arrayResult[n-1][m-1];
    }
}

 

posted @ 2020-03-28 16:17  大朱123  阅读(206)  评论(0编辑  收藏  举报