动态规划法之杨辉三角(Java)

一、动态规划法概述

动态规划法是一种解决多阶段决策过程最优化问题的数学方法。

二、基本思想

  • 动态规划的基本思想是把原问题分解成若干个子问题,先求解子问题,然后从这些子问题的解得到原问题的解。它保存子问题的解,避免重复计算,从而提高算法效率。
  • 具体而言,动态规划通常会寻找问题的最优子结构性质和子问题重叠性质。最优子结构性质是指问题的最优解可以由子问题的最优解组合而成;子问题重叠性质是指在求解过程中,不同的子问题会被重复求解多次。

三、求解过程

  • 划分阶段:按照问题的时间顺序或空间特征,将问题划分为若干个阶段。

  • 确定状态:定义每个阶段的状态,状态应能够描述该阶段的决策结果和问题的特征。

  • 确定决策:明确在每个阶段可以采取的决策,决策会导致状态的转移。

  • 写出状态转移方程:根据问题的逻辑关系,写出状态转移方程,即从一个状态转移到另一个状态的规则。

  • 确定初始状态和边界条件:明确问题的初始状态和边界条件,以便从初始状态开始逐步求解。

  • 求解问题:通常采用自底向上或自顶向下的方法,根据状态转移方程逐步求解每个状态的值,最终得到问题的最优解。

四、适用场合

  • 资源分配问题:如在有限的资源下如何分配任务,使得总收益最大。

  • 路径规划问题:寻找图中两点之间的最短路径。

  • 背包问题:在给定容量的背包中,如何选择物品使得总价值最大。

  • 生产调度问题:安排生产任务,以最小化生产时间或成本。

五、杨辉三角问题描述

给定一个非负整数 numRows生成「杨辉三角」的前 numRows 行。

(在「杨辉三角」中,每个数是它左上方和右上方的数的和)

题解示例

示例 1:

输入: numRows = 5
输出: [[1],[1,1],[1,2,1],[1,3,3,1],[1,4,6,4,1]]

示例 2:

输入: numRows = 1
输出: [[1]]

六、用动态规划法解决杨辉三角问题的思路

  1. 定义状态:我们可以将杨辉三角的每一行看作一个状态。用resultList.get(i)表示杨辉三角中第i行的状态,其中每一行又是一个由整数组成的列表,列表中的每个元素对应杨辉三角该行中的某一列数字。

  2. 确定状态转移方程:对于杨辉三角,状态转移方程体现为代码中的rowNow.add(rowLast.get(j - 1) + rowLast.get(j)),即当j > 0 && j < i时,当前行(用rowNow表示)第j个位置的数字等于上一行(用rowLast表示)第j - 1个位置的数字与第j个位置的数字之和;边界情况为每一行的第一个和最后一个数字都是1,对应代码中的rowNow.add(1)分别在循环前后执行。

  3. 初始化边界条件:第一行只有一个数字1,通过代码中的List<Integer> row1 = new ArrayList<>(); row1.add(1); resultList.add(row1);实现初始化,即resultList.get(0)对应杨辉三角的第一行,初始状态为dp[0][0] = 1

  4. 求解问题:通过两层循环来求解杨辉三角。外层循环for(int i = 1; i < numRows; i++)控制行数,内层循环for(int j = 1; j < i; j++)控制列数。根据状态转移方程逐步计算出每一行的数字,最终得到杨辉三角的前numRows行,由return resultList;返回结果。

七、Java 代码实现

class Solution {
    public List<List<Integer>> generate(int numRows) {
        // 创建一个用于存储结果的列表
        List<List<Integer>> resultList = new ArrayList<>();
        // 如果输入的行数小于等于 0,直接返回空结果列表
        if (numRows <= 0) {
            return resultList;
        }
        // 创建并初始化第一行,添加数字 1 到第一行列表中
        List<Integer> row1 = new ArrayList<>();
        row1.add(1);
        // 将第一行添加到结果列表中
        resultList.add(row1);
        // 从第二行开始生成杨辉三角的后续行
        for (int i = 1; i < numRows; i++) {
            // 创建当前行的列表
            List<Integer> rowNow = new ArrayList<>();
            // 当前行的第一个数字总是 1,将其添加到当前行列表中
            rowNow.add(1);
            // 获取上一行的列表
            List<Integer> rowLast = resultList.get(i - 1);
            // 遍历当前行中间的数字(除了首尾的 1)
            for (int j = 1; j < i; j++) {
                // 根据上一行对应位置的数字计算当前位置的数字,即上一行当前位置的前一个数字与当前位置数字之和
                rowNow.add(rowLast.get(j - 1) + rowLast.get(j));
            }
            // 当前行的最后一个数字总是 1,将其添加到当前行列表中
            rowNow.add(1);
            // 将当前行添加到结果列表中
            resultList.add(rowNow);
        }
        // 返回生成的杨辉三角的所有行
        return resultList;
    }
}

在这段代码中,结果列表resultList是一个包含多行的列表,每一行又是一个包含若干整数的列表,所以可以理解为结果列表在一定程度上类似一个二维结构,既有行也有列的概念。

具体来说:

  • 从行的角度看,resultList中的每个元素都是一行数据,例如在生成杨辉三角的过程中,通过不断添加新的行(内部是一个整数列表)来构建整个杨辉三角。

  • 从列的角度看,每一行内部的整数列表中的元素可以看作是这一行中的不同列的值。

八、List相关

单个List<Integer>

定义

List<Integer> singleList = new ArrayList<>();

List<Integer> singleList = Arrays.asList(1, 2, 3);

List<Integer> singleList = new ArrayList<>(Arrays.asList(1, 2, 3));

使用

      1.添加元素:
   singleList.add(4);
      2.获取元素:
   int element = singleList.get(1); // 获取索引为 1 的元素

嵌套List<List<Integer>>

在 Java 中,List<List<Integer>>是一个嵌套的列表类型,表示一个包含多个列表的列表,其中每个内部列表中存储的元素类型为Integer。这种类型通常用于表示二维数据结构,比如矩阵、表格等。在一些场景下,当需要处理多行多列的数据,且每行的数据类型相同但行数不确定时,就可以使用这种嵌套列表的结构。

创建方法
     List<List<Integer>> newList = new ArrayList<>();
添加元素方法
  • 向外部列表添加一个新的内部列表:
     List<Integer> innerList = new ArrayList<>();
     innerList.add(1);
     innerList.add(2);
     nestedList.add(innerList);
  • 向已有的内部列表添加元素:
     if (!nestedList.isEmpty()) {
         nestedList.get(0).add(3);
     }

访问元素
  • 访问外部列表中的某个内部列表:
     List<Integer> specificInnerList = nestedList.get(1);

  • 访问内部列表中的特定元素:
     int element = nestedList.get(2).get(1);

遍历

使用传统的 for 循环遍历外部列表和内部列表:

     for (List<Integer> innerList : nestedList) {
         for (Integer element : innerList) {
             System.out.print(element + " ");
         }
         System.out.println();
     }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值