ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

LeeCode 动态规划(一)

2022-09-09 13:02:42  阅读:169  来源: 互联网

标签:初始化 遍历 int LeeCode cost 数组 动态 规划 dp


简述

如果某一问题存在很多重叠子问题,使用动态规划是非常有效的。

动态规划与贪心

  • 贪心:每次都选择局部最优解
  • 动态规划:每个状态都是由前一个状态推导得到

动态规划解题步骤

  1. 确定 dp数组 及下标的含义
  2. 确定递推公式
  3. dp数组 初始化
  4. 确定遍历顺序

LeeCode 509:斐波那契数

题目描述

斐波那契数(通常使用 F(n) 表示)形成的序列称为斐波那契数列。该数列由 01 开始,后面的每一项数字都是前面两项数字的和。即 \(F(0) = 0;F(1) = 1; F(n) = F(n - 1) + F(n - 2) \quad n > 1\)。给定 n ,请计算 F(n)

建立模型

  • 确定dp数组及下标的含义, 这里省略了dp数组(当前状态只和前两个状态有关), 借用两个参数a, b来代替. 数组的含义为 F(i)
  • 确定递推公式, dp[i] = dp[i - 1] + dp[i - 2] 即 res = a + b
  • dp数组初始化, dp[0] = 0, dp[1] = 1 即 a = 0, b = 1
  • 确定遍历顺序, 2 ~ n顺序遍历

代码实现

public int fib(int n) {
  if (n <= 1) {
    return n;
  }

  int a = 0, b = 1;
  int res = 1;
  for (int i = 2; i <= n; i++) {
    res = a + b;
    a = b;
    b = res;
  }

  return res;
}

LeeCode 70:爬楼梯

题目描述

假设你正在爬楼梯,需要 n 阶你才能到达楼顶。每次你可以爬 12 个台阶。你有多少种不同的方法可以爬到楼顶呢?

建立模型

此题与斐波那契数列属于同一题, 不过此次笔者没有省略 dp数组.

  • 确定dp数组及下标的含义, 数组的含义为爬到第 i 阶楼梯的方法数
  • 确定递推公式 dp[i] = dp[i - 1] + dp[i - 2]
  • dp数组初始化, dp[1] = 1, dp[2] = 2
  • 确定遍历顺序, 3~n 顺序遍历

代码实现

public int climbStairs(int n) {
  if (n <= 2) {
    return n;
  }

  int[] dp = new int[n + 1];

  // dp数组初始化
  dp[1] = 1;
  dp[2] = 2;

  for (int i = 3; i < dp.length; i++) {
    dp[i] = dp[i - 1] + dp[i - 2];
  }

  return dp[n];
}

LeeCode 746:使用最小花费爬楼梯

题目描述

给你一个整数数组 cost ,其中 cost[i] 是从第 i 个台阶向上爬需要支付的费用。一旦你支付此费用,即可选择向上爬一个或者两个台阶。

你可以选择从下标为 0 或 下标为 1 的台阶开始爬楼梯。

请你计算并返回达到楼梯顶部的最低花费。

建立模型

  • 确定dp数组及下标的含义, 数组的含义为到达第 i 个台阶的最小花费
  • 确定递推公式 dp[i] = Math.min(dp[i - 1] + dp[i - 2]) + cost[i]
  • 初始化dp数组, dp[0] = cost[0], dp[1] = cost[1]
  • 确定遍历顺序, 从 2~n 顺序遍历

代码实现

public int minCostClimbingStairs(int[] cost) {
  if (cost.length == 2) {
    return Math.min(cost[0], cost[1]);
  }

  int[] dp = new int[cost.length];

  dp[0] = cost[0];
  dp[1] = cost[1];

  for (int i = 2; i < dp.length; i++) {
    /**
     * 递推数组
     * 爬到第 i 阶楼梯的方式包括 从 i - 1 阶爬一个台阶 和 从 i - 2 阶爬两个台阶
     * 取其中的花费较小者
     */
    dp[i] = Math.min(dp[i - 1], dp[i - 2]) + cost[i];
  }

  /**
   * 到达顶部的花费选择 倒数第一个台阶和倒数第二个台阶的较小者
   */
  return Math.min(dp[cost.length - 1], dp[cost.length - 2]);
}

LeeCode 62:不同路径

题目描述

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

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

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

建立模型

  • 确定dp数组及下标含义, 数组的含义为到达第i行第j列的路径总数
  • 确定递推公式 dp[i][j] = dp[i - 1][j] + dp[i][j - 1]
  • 初始化dp数组, 第0行和第0列全都初始化为1
  • 确定遍历顺序, 外循环从左至右, 内循环从上至下

代码实现

public int uniquePaths(int m, int n) {
  int[][] dp = new int[m][n];

  // dp数组初始化
  // 第 0 行初始化
  for (int i = 0; i < n; i++) {
    dp[0][i] = 1;
  }
  // 第 0 列初始化
  for (int j = 0; j < m; j++) {
    dp[j][0] = 1;
  }

  for (int i = 1; i < m; i++) {
    for (int j = 1; j < n; j++) {
      dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
    }
  }

  return dp[m - 1][n - 1];

}

LeeCode 63:不同路径II

题目描述

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

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

现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径?

网格中的障碍物和空位置分别用 1 和 0 来表示。

建立模型

  • 确定dp数组及下标的含义, 数组的含义为到达第i行第j列的路径总数
  • 确定递推公式 dp[i][j] = dp[i - 1][j] + dp[i][j - 1], 对于网格中的障碍物有dp[i][j] = 0
  • 初始化dp数组, dp[0][0]根据是否有障碍物初始化为0或1, 对于第0行, 则根据左边是否有障碍物初始化为0或1, 第0列则根据上边是否有障碍无初始化为0或1
  • 确定遍历顺序, 外循环从左至右, 内循环从上至下

代码实现

public int uniquePathsWithObstacles(int[][] obstacleGrid) {
  int m = obstacleGrid.length;
  int n = obstacleGrid[0].length;

  int[][] dp = new int[m][n];

  // dp数组初始化
  if (obstacleGrid[0][0] == 1) {
    dp[0][0] = 0;
  }
  else {
    dp[0][0] = 1;
  }

  // 第 0 行初始化
  for (int i = 1; i < n; i++) {
    if (obstacleGrid[0][i] == 1) {
      dp[0][i] = 0;
    }
    else {
      dp[0][i] = dp[0][i - 1];
    }
  }

  // 第 0 列初始化
  for (int j = 1; j < m; j++) {
    if (obstacleGrid[j][0] == 1) {
      dp[j][0] = 0;
    }
    else {
      dp[j][0] = dp[j - 1][0];
    }
  }

  // 遍历dp数组
  for (int i = 1; i < m; i++) {
    for (int j = 1; j < n; j++) {
      if (obstacleGrid[i][j] == 1) {
        dp[i][j] = 0;
      }
      else {
        dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
      }
    }
  }

  return dp[m - 1][n - 1];
}

LeeCode 343:整数拆分

题目描述

给定一个正整数 n ,将其拆分为 k正整数 的和( k >= 2 ),并使这些整数的乘积最大化。

返回 你可以获得的最大乘积

建立模型

  • 确定dp数组及下标的含义, 数组的含义为 i 可拆分成的乘积最大值
  • 确定递推公式 dp[i] = MAX(Math.max(j * (i - j), j * dp[i - j])) (1 <= j < i)
  • 初始化dp数组, 全都初始化为0
  • 确定遍历顺序, i -> 2~n, j -> 1~(i-1)

代码实现

public int integerBreak(int n) {
  int[] dp = new int[n + 1];
  
  for (int i = 2; i <= n; i++) {
    int max_value = 0;
    for (int j = 1; j < i; j++) {
      /**
       * j * (i - j)   表示 (i - j) 不继续拆分的乘积值
       * j * dp[i - j] 表示 (i - j) 继续拆分的乘积值
       */
      max_value = Math.max(max_value, Math.max(j * (i - j), j * dp[i - j]));
    }
    dp[i] = max_value;
  }

  return dp[n];
}

LeeCode 96:不同的二叉搜索树

题目描述

给你一个整数 n ,求恰由 n 个节点组成且节点值从 1n 互不相同的 二叉搜索树 有多少种?返回满足题意的二叉搜索树的种数。

建立模型

  • 确实dp数组及其下标的含义, 数组的含义为 i 个节点能构成的不同的二叉搜索树

  • 初始化dp数组, dp[0] = dp[1] = 1, 空树或单节点的树只有一种

  • 确定递推公式, dp[i] += dp[j] * dp[i - j - 1], 该公式的意思为依次遍历每个 \

    数字将其作为根节点, 将 1~j-1 作为左子树, j+1~i 作为右子树, 同时左右子树也 \

    需要满足是二叉搜索树, 且由于根值不同, 可以保证每棵二叉搜索树都是不同的, 所以 \

    该问题就转化成了两个规模更小的问题。

  • 确定遍历顺序, i -> 2~n, j -> 1~i

代码实现

public int numTrees(int n) {
  int[] dp = new int[n + 1];

  // dp数组初始化
  dp[0] = 1;
  dp[1] = 1;

  // 遍历数组
  for (int i = 2; i <= n; i++) {
    for (int j = 1; j <= i; j++) {
      dp[i] += dp[j - 1] * dp[i - j];
    }
  }

  return dp[n];
}

标签:初始化,遍历,int,LeeCode,cost,数组,动态,规划,dp
来源: https://www.cnblogs.com/ylyzty/p/16672472.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有