动态规划问题实验:数塔问题

目录

  • 前言
  • 实验内容
  • 实验流程
  • 实验过程
    • 实验分析
    • 伪代码
    • 代码实现
    • 分析算法复杂度
    • 用例测试
  • 总结

前言

动态规划是一种解决复杂问题的方法,它将一个问题分解为若干个子问题,然后从最简单的子问题开始求解,逐步推导出更复杂的子问题的解,最终得到原问题的最优解。动态规划的关键是找到子问题之间的递推关系,以及确定合适的边界条件和初始值。

数塔问题是一个经典的动态规划问题,它描述了一个由数字组成的三角形结构,要求从顶部开始向下走,每次只能走到相邻的位置,最终到达底部,使得经过的数字之和最大。数塔问题可以用一个二维数组来表示,其中第i行有i个元素,表示第i层的数字。例如:

9
12 15
10 6 8
2 18 9 5
16 12 18 10 8

数塔问题的一个可能的最优路径是:9 -> 15 -> 8 -> 9 -> 18,其和为59。

实验内容

给出一个数塔,从该数塔的顶层出发,在每一个节点可以选择向左走或向右走,一直走到该数塔的最底层,找出一条路径,使得路径上的数值和最大,输出最大数值及其路径,输出时要求有文字说明。请任选一种语言编写程序实现上述算法,并分析其算法复杂度。

实验流程

根据实验内容设计算法伪代码进行算法描述;
利用C++/C/Java等编程语言对算法伪代码进行工程化实现;
输入测试用例对算法进行验证;
列出算法时间复杂度模型并与计算机运行统计时间进行对比分析。

实验过程

实验分析

这个问题是一个典型的动态规划问题,动态规划的基本思想是将待求解问题分解成若干个子问题,先求解子问题,然后从这些子问题的解得到原问题的解。动态规划通常需要保存已解决的子问题的答案,以避免重复计算,节省时间。

对于数塔问题,我们可以从下往上逐层计算每个节点到底层的最大路径和,并记录下每个节点选择的方向。最后从顶层开始根据方向输出路径即可。

例如,给定一个数塔如下:

7
3 8
8 1 0
2 7 4 4
4 5 2 6 5

我们可以用一个二维数组a来存储数塔中的数字,用另一个二维数组f来存储每个节点到底层的最大路径和,用另一个二维数组p来存储每个节点选择的方向(0表示左,1表示右)。初始化时,f的最后一行就是a的最后一行,p可以任意初始化。

然后我们从倒数第二行开始,逐层向上计算f和p。对于每个节点a[i][j],我们比较它下面两个节点f[i+1][j]和f[i+1][j+1]的大小,选择较大者作为它到底层的最大路径和,并记录下选择的方向。具体地,有如下状态转移方程:

f[i][j] = max(f[i+1][j], f[i+1][j+1]) + a[i][j]
p[i][j] = f[i+1][j] > f[i+1][j+1] ? 0 : 1
复制
当我们计算完所有的f和p后,f[0][0]就是数塔顶层到底层的最大路径和。我们可以从顶层开始根据p输出路径。具体地,我们用两个变量i和j表示当前节点的位置,初始为0和0。然后我们输出a[i][j],并根据p[i][j]更新i和j(如果p[i][j]为0,则i=i+1,j=j;如果p[i][j]为1,则i=i+1,j=j+1)。重复这个过程直到i等于数塔的高度。

例如,对于上面给定的数塔,计算完f和p后得到如下结果:

f:
30
23 21
20 13 10
7 12 10 10
4 5 2 6 5

p:
1
0 1
0 0 0
0 0 0 0


则最大路径和为30,路径为7->3->8->7->5。

伪代码

// Define a constant for the maximum height of the tower
constant MAXN = 100

// Declare a two-dimensional array to store the numbers in the tower
array a[MAXN][MAXN]

// Declare a two-dimensional array to store the maximum path sum from each node to the bottom
array f[MAXN][MAXN]

// Declare a two-dimensional array to store the direction of each node (0 for left, 1 for right)
array p[MAXN][MAXN]

// Define the main program
function main()
  // Declare an integer variable to store the height of the tower
  integer n
  // Input the height from the user
  input n

  // Input the numbers in the tower from the user
  for i from 0 to n-1 // Loop through each row
    for j from 0 to i // Loop through each column
      input a[i][j]

  // Initialize f and p
  for j from 0 to n-1 // Loop through the last row
    f[n-1][j] = a[n-1][j] // The last row of f is the same as the last row of a
    p[n-1][j] = -1 // The last row of p has no direction to choose

  // Compute f and p from bottom to top
  for i from n-2 to 0 // Loop through each row except the last one in reverse order
    for j from 0 to i // Loop through each column
      if f[i+1][j] > f[i+1][j+1] // If the left child is larger than the right child
        f[i][j] = f[i+1][j] + a[i][j] // Choose the left child as the maximum path sum and add the current node value
        p[i][j] = 0 // Record the direction as left
      else // Otherwise
        f[i][j] = f[i+1][j+1] + a[i][j] // Choose the right child as the maximum path sum and add the current node value
        p[i][j] = 1 // Record the direction as right

  // Output the maximum path sum and its path
  print "最大路径和为:" + f[0][0] // The maximum path sum is f[0][0]
  print "路径为:"
  i = 0, j = 0 // The current node position (starting from the top)
  while i < n // Loop until reaching the bottom row
    print a[i][j] + " " // Output the current node value
    if p[i][j] == -1 // If there is no direction to choose, break the loop (reached the bottom row)
      break
    if p[i][j] == 0 // If the direction is left, update the position to the next row and same column
      i = i + 1
    else // If the direction is right, update the position to the next row and right column
      i = i + 1
      j = j + 1


代码实现

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#define MAXN 100 // 数塔最大高度

int a[MAXN][MAXN]; // 存储数塔中的数字
int f[MAXN][MAXN]; // 存储每个节点到底层的最大路径和
int p[MAXN][MAXN]; // 存储每个节点选择的方向(0表示左,1表示右)

int main() {
    int n; // 数塔高度
    scanf("%d", &n); // 输入高度

    // 输入数塔中的数字
    for (int i = 0; i < n; i++) {
        for (int j = 0; j <= i; j++) {
            scanf("%d", &a[i][j]);
        }
    }

    // 初始化f和p
    for (int j = 0; j < n; j++) {
        f[n-1][j] = a[n-1][j]; // 最后一行就是a的最后一行
        p[n-1][j] = -1; // 最后一行没有方向可选
    }

    // 自底向上计算f和p
    for (int i = n-2; i >= 0; i--) { // 倒数第二行开始往上
        for (int j = 0; j <= i; j++) { // 每行有i+1个节点
            if (f[i+1][j] > f[i+1][j+1]) { // 如果左边大于右边
                f[i][j] = f[i+1][j] + a[i][j]; // 则选择左边作为最大路径和,并加上当前节点值
                p[i][j] = 0; // 记录方向为左
            } else { // 否则选择右边作为最大路径和,并加上当前节点值
                f[i][j] = f[i+1][j+1] + a[i][j];
                p[i][j] = 1; // 记录方向为右
            }
        }
    }

    // 输出最大路径和及其路径
    printf("最大路径和为:%d\n", f[0][0]); // 最大路径和就是f[0][0]
    printf("路径为:");
    int i = 0, j = 0; // 当前节点位置(从顶层开始)
    while (i < n) { // 直到到达底层结束循环
        printf("%d ", a[i][j]); // 输出当前节点值
        if (p[i][j] == -1) break; // 如果没有方向可选,则结束循环(已经到达底层)
        if (p[i][j] == 0) { // 如果方向为左,则更新位置为下一行同一列
            i++;
        } else { // 如果方向为右,则更新位置为下一行右一列
            i++;
            j++;
        }
    }
    printf("\n");

    return 0;
}

分析算法复杂度

时间复杂度:由于需要遍历整个数塔两次(一次输入数字,一次计算f和p),所以时间复杂度为O(n^2),其中n为数塔高度。
空间复杂度:由于需要使用三个二维数组来存储数字、最大路径和、方向信息,所以空间复杂度也为O(n^2),其中n为数塔高度。

用例测试

请添加图片描述

总结

本实验的目的是掌握动态规划的基本思想和方法,以及如何应用动态规划解决数塔问题。数塔问题是一个经典的动态规划问题,给定一个由n层数字组成的三角形,从顶层出发,在每一层可以选择左边或右边的数字,一直走到底层,求出所经过的数字之和的最大值。本实验采用自底向上的方法,从底层开始,计算每个位置到底层的最大值,并存储在一个二维数组中,最后得到顶层到底层的最大值。本实验还要求输出最大值对应的路径,即所选择的数字序列。为了实现这一功能,需要在计算过程中记录每个位置选择的方向,并在计算完成后从顶层开始回溯,输出所选择的数字。

本实验的难点在于理解动态规划的原理和过程,以及编写正确和高效的代码。动态规划是一种将复杂问题分解为子问题,并利用子问题之间的关系和重复性,避免重复计算,从而提高效率的方法。动态规划适用于具有最优子结构和重叠子问题的问题。最优子结构指的是原问题的最优解可以由子问题的最优解构成,重叠子问题指的是在求解过程中会多次遇到相同的子问题。数塔问题满足这两个条件,因为每个位置到底层的最大值只取决于它下面一层相邻两个位置的最大值,而且在计算过程中会多次计算同一个位置到底层的最大值。因此,可以用动态规划来解决数塔问题。

本实验还需要注意代码的编写规范和风格,以及程序的可读性和可维护性。代码应该遵循统一和清晰的命名规则,使用适当的注释和缩进,避免冗余和无用的代码,使用合理的数据结构和算法,处理好边界情况和异常情况等。程序应该具有良好的模块化和封装性,将不同功能分离为不同函数或类,并提供清晰和完整的接口和文档。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/22688.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

设计原则-单一职责原则

在编程大环境中&#xff0c;评价代码组织方式质量的好坏涉及到各个方面&#xff0c;如代码的可读性、可维护性、可复用性、稳定性等各个方面。而在面向对象语言中也可以通过以下各个方面&#xff1a; 类中方法的设计类中属性的设计类(接口、抽象类、普通类)的设计类与类之间的…

十万条数据,后端不分页咋办!(如何优化长列表渲染)

十万条数据&#xff0c;后端不分页咋办&#xff01;&#xff08;如何优化长列表渲染&#xff09; 长列表是什么&#xff1f; 我们通常把一组数量级很大的数据叫做长列表&#xff0c;比如渲染一组上千条的数据&#xff0c;我们以数组的形式拿到这些信息&#xff0c;然后遍历渲…

正点原子ALPHA开发板核心资源分析

目录 正点原子ALPHA开发板核心资源分析I.MX6ULL实物图对比SOC 主控芯片&#xff08;MCIMX6Y2CVM08AB&#xff09;NAND FLASHEMMCDDR3L 正点原子ALPHA开发板核心资源分析 I.MX6ULL实物图对比 I.MX6ULL NAND BTB 接口核心板资源图与 I.MX6ULL EMMC BTB 接口核心板资源图如上图&a…

电商项目9:新增商品

电商项目9&#xff1a;新增商品 1、前端1.1、修复前端组件通信问题1.2、引入其他前端代码1.3、会员等级列表1.4、当前分类关联的所有品牌 2、后端2.1、会员系统搭建&#xff08;注册与发现&#xff09;2.2、当前分类关联的所有品牌2.3、获取分类下所有分组&关联属性 1、前端…

shell sed命令

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 sed 命令sed 编辑器sed 的工作流程的三个过程命定格式常用选项常用操作 实验操作打印内容使用地址删除行替换插入 sed 命令 sed 编辑器 sed是一种流编辑器&#x…

听我一句劝,别去外包,干了6年,废了....

先说一下自己的情况&#xff0c;大专生&#xff0c;18年通过校招进入湖南某软件公司&#xff0c;干了接近6年的功能测试&#xff0c;今年年初&#xff0c;感觉自己不能够在这样下去了&#xff0c;长时间呆在一个舒适的环境会让一个人堕落!而我已经在一个企业干了6年的功能测试&…

中国移动董宁:深耕区块链的第八年,我仍期待挑战丨对话MVP

区块链技术对于多数人来说还是“新鲜”的代名词时&#xff0c;董宁已经成为这项技术的老朋友。 董宁2015年进入区块链领域&#xff0c;现任中国移动研究院技术总监、区块链首席专家。作为“老友”&#xff0c;董宁见证了区块链技术多个爆发式增长和平稳发展的阶段&#xff0c;…

Doxygen 源码分析: SymbolMap类

2023-05-21 10:59:35 ChrisZZ imzhuofoxmailcom Hompage https://github.com/zchrissirhcz 文章目录 1. Doxygen 版本2. SymbolMap 类概要3. 添加符号: SymbolMap<T>::add()4. 删除符号: SymbolMap<T>::remove()5. 符号查找: SymbolMap<T>::find()6. 哪里用了…

什么是半实物仿真平台自动驾驶半实物仿真平台有哪些?

文章目录 半实物仿真平台介绍自动驾驶半实物仿真平台介绍1.CARLA2.AirSim3.LGSVL Simulator 半实物仿真平台介绍 半实物仿真平台是一种综合利用虚拟仿真和实际硬件设备的仿真系统。它将虚拟环境和真实硬件设备结合起来&#xff0c;旨在提供更真实、更准确的仿真体验。 在半实…

基于html+css的图展示90

准备项目 项目开发工具 Visual Studio Code 1.44.2 版本: 1.44.2 提交: ff915844119ce9485abfe8aa9076ec76b5300ddd 日期: 2020-04-16T16:36:23.138Z Electron: 7.1.11 Chrome: 78.0.3904.130 Node.js: 12.8.1 V8: 7.8.279.23-electron.0 OS: Windows_NT x64 10.0.19044 项目…

Boundary IoU:Improving Object-Centric Image Segmentation Evaluation总结笔记

Boundary IoU:Improving Object-Centric Image Segmentation Evaluation&#xff08;边界Iou&#xff1a;改进以对象为中心的图像分割评价&#xff09; 目录 一、论文出发点 二、论文核心思想 三、相关工作 四、敏感度分析 五、Boundary IoU定义和实验证明 六、应用 七…

基于Gabor-小波滤波深度图表面法线的特征提取算法【通过正常Gabor-小波的直方图进行2D或3D特征提取】研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

Redis+Lua脚本防止超卖

超卖就是因为查询库存和扣减库存两个操作不是原子性操作&#xff0c;通过rua脚本执行这两个操作可以保证这两个操作原子性 判断库存量是不是大于等于1&#xff0c;如果大于等于1对库存减1&#xff0c;否则就不去减库存 StringBuilder sb new StringBuilder();sb.append("…

【JAVA进阶】Stream流

&#x1f4c3;个人主页&#xff1a;个人主页 &#x1f525;系列专栏&#xff1a;JAVASE基础 目录 1.Stream流的概述 2.Stream流的获取 3.Stream流的常用方法 1.Stream流的概述 什么是Stream流&#xff1f; 在Java 8中&#xff0c;得益于Lambda所带来的函数式编程&#xff0…

使用go语言构建区块链 Part2.工作量证明

英文源地址 简介 在上一篇文章中, 我们构建了一个非常简单的数据结构, 这是区块链数据库的本质.并且我们可以通过它们之间的链式关系来添加区块: 每个区块都链接到前一个区块.哎, 我们的区块链实现有一个重大缺陷: 向链中添加区块既容易又便捷. 区块链和比特币的关键之一是增…

面对当下各种不确定性,如何面对,每天很忙碌,不慌

&#xff08;点击即可收听&#xff09; 疫情时期,都难,疫情之后,发现还更难 随着互联网的热度的下降,各大小公司纷纷勒紧裤腰带,受打击最大的无疑是底层打工人 每天一打开手机,会发现,一些大厂裁员信息霸榜头条,年龄也是一道坎 刚刚看到一个大v发的&#xff1a; 一个原先是跨国…

如何在 OpenSUSE 上安装 VirtualBox 7?

VirtualBox 是一款开源的虚拟化软件&#xff0c;允许用户在单个计算机上运行多个操作系统。本文将详细介绍如何在 OpenSUSE 上安装 VirtualBox 7。以下是安装过程的步骤&#xff1a; 步骤一&#xff1a;下载 VirtualBox 7 首先&#xff0c;我们需要下载 VirtualBox 7 的安装包…

真题详解(语法分析输入记号流)-软件设计(八十)

真题详解&#xff08;求叶子结点数&#xff09;-软件设计&#xff08;七十九)https://blog.csdn.net/ke1ying/article/details/130787349?spm1001.2014.3001.5501 极限编程XP最佳实践&#xff1a; 测试先行、 按日甚至按小时为客户提供可运行的版本。 组件图的 插座 和插头…

Pytorch的CNN,RNNLSTM

CNN 拿二维卷积举例&#xff0c;我们先来看参数 卷积的基本原理&#xff0c;默认你已经知道了&#xff0c;然后我们来解释pytorch的各个参数&#xff0c;以及其背后的计算过程。 首先我们先来看卷积过后图片的形状的计算&#xff1a; 参数&#xff1a; kernel_size &#xff…

使用Linkage Mapper工进行物种分布建模的步骤详解(含实际案例分析)

✅创作者:陈书予 🎉个人主页:陈书予的个人主页 🍁陈书予的个人社区,欢迎你的加入: 陈书予的社区 🌟专栏地址: Linkage Mapper解密数字世界链接 文章目录 引言:一、介绍二、数据准备2.1 物种分布数据获取2.2 环境变量数据获取2.3 数据预处理