C++算法初级10——动态规划

news2024/10/2 8:37:30

C++算法初级10——动态规划

文章目录

  • C++算法初级10——动态规划
    • 最优化问题
    • 动态规划分析流程和条件

最优化问题

生活中我们常常遇到这样一些问题:
在这里插入图片描述
看到上面的例子,我们发现这些问题都是在最大化(或者最小化)某个指标:最小化平均等待时间、最小化总旅行路程、最大化背包里的物品个数。这种类型的问题我们一般称为最优化问题。

最优化问题(optimization problem)是在一些约束下,通过进行一些决策,使得最终获益最大(或损失最小)的一类问题。

数字金字塔问题
观察下面的数字金字塔:在这里插入图片描述
现在,需要我们找到一种方法,查找从最高点到底部任意处结束的路径,使路径经过数字的和最大。

注:每一步可以走到左下方的点也可以到达右下方的点。

比如,在上面的样例中,从 7→3→8→7→5的路径经过数字的和最大。

现在,我们把数字金字塔转化成一个算法问题,就变成了:

给定一个n层的金字塔,求一条从最高点到底层任意点的路径使得路径经过的数字之和最大。

注:每一步可以走到左下方的点也可以到达右下方的点。

分析
基本思路

我们按照下面的步骤依次观察这个问题的结构:

  1. 首先,因为我们可以走到最下面一层的任意点。那么,只要我们能够分别求出到达每个点的最大路径,然后在所有点里面取最大值即可解决这个问题。
  2. 下面我们仅考虑走到最下面一层确定点的最大路径。假设我们现在想求走到最下面一层中间的2的最大路径,最暴力的方法就是列举所有走到2的路径,然后取路径和最大的一条作为答案。所以,所有走到2的路径如下:在这里插入图片描述
    所以,最终走到2的路径里面,数字和最大是27
  3. 我们进一步观察所有走到2的路径。因为它的路径只可能从上面两个方向走下来。所以如下图,所有走到2的路径可以被分成两类:从7走过过来的路径和从4走过来的路径。
    在这里插入图片描述
    对于所有结尾是7的路径
    在这里插入图片描述
    我们只需要接上一段→2,就可以变成从最上面的结点走到2的路径:在这里插入图片描述
    但是,如果我们已经知道“到达7的路径”里面第2条路经和第3条路径不如第1条路径,是不是就可以直接舍弃下面两条,只考虑经由第1条路径走到2的情况?也就是说,为了求所有“经由
    7走到2”的路径里面,我们只需要计算第一条路径即可。
    同样,对于所有“经由4走到2”的路径里面,我们也只需要挑选到达4最大的一条,然后将→2接在后面。
    在这里插入图片描述
  4. 那么因为“从三角形顶端到达2”的路径里面,只有上面两种情况,所以,它们之间的的较大值就是到达2的最大路径,也就是 m a x { 27 , 22 } = 27 max\{27,22\}=27 max{27,22}=27
  5. 可是如此一来,我们不需要枚举所有”从顶点到达2“的路径。但为了找出两种情况下各自的最大值,看起来我们仍需要枚举”从顶点到达7“和”从顶点到达4“的路径。

但是,我们发现,找到”从顶点到达7“和”从顶点到达4“的最大路径,就是一个和原问题”从顶点到达2“结构相似的问题!另外,由于7和4的位置比2要少一行,所以实际上,这两个问题是一个规模更小的问题!也就是说,这两个问题是原问题的一个子问题!那么,我们利用和上面类似的分析思路,就可以不用枚举所有到达7和4的路径了。

这里,我们把上一步的基本思路形式化成一个严谨的算法:

  1. 我们用a[i][j]存储数字金字塔第i行第j列的数字,用f[i][j]表示”从顶点到达第i行第j列“的所有路径中最大的数字和。

  2. 对于顶点,因为它是起始点,所以f[1][1] = a[1][1]。

  3. 因为到达(i, j)的路径最多只可能从(i - 1, j - 1)和(i - 1, j)两个点走过来(如果在三角形的最左边或者最右边,那么它的上一个结点就只有一种情况),所以,我们有下面的关系:

 f[i][j] = f[i - 1][j - 1] + a[i][j]// i == j
 f[i][j] = f[i - 1][j]     + a[i][j]// j == 1
 f[i][j] = max(f[i - 1][j - 1], f[i - 1][j]) + a[i][j]// otherwise

那么,观察这个等式,会发现如果我们已知f[i - 1][j - 1]和f[i - 1][j],就可以求出f[i][j]。所以实际上,它有点像一个特殊形式的递推:有初始状态和递推关系。那么我们通过一个二重循环就可以求出所有f[i][j]。
4. 最后,我们输出所有f[n][j]对于所有(1<=j && j<=n)的最大值即可。

#include <bits/stdc++.h>
#define N 1005
#define M 110
using namespace std; 
int n = 5; // 数字金字塔有5行/* 数字金字塔:73 88 1 02 7 4 44 5 2 6 5 */
int a[N][N] = {{0}, {0, 7}, {0, 3, 8}, {0, 8, 1, 0}, {0, 2, 7, 4, 4}, {0, 4, 5, 2, 6, 5}};
int f[N][N];

int main()
{
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= i; j++)
        {
            if (i == j) // 最后一行的数字只能从上一行的最后一个数字走过来
            {
                f[i][j] = f[i - 1][j - 1] + a[i][j];
                continue;
            }
            if (j == 1) // 第一列的数字只能从上一行的第一个数字走过来
            {
                f[i][j] = f[i - 1][j] + a[i][j];
                continue;
            }
            f[i][j] = max(f[i - 1][j - 1], f[i - 1][j]) + a[i][j];
        }
    }
    int ans = 0;
    for (int i = 1; i <= n; i++)// 从最后一行中找最大值
        ans = max(ans, f[n][i]);
    cout << ans << endl;
    return 0;
}

动态规划过程可以进行简化
在这里插入图片描述

在这里插入图片描述

动态规划分析流程和条件

首先是动态规划分析流程:
在数字金字塔的分析中我们发现,用动态规划解决问题的过程,就是一个把原问题的过程变成一个阶段性决策的过程。

比如在数字金字塔问题中,路径每往下延伸一行,我们就进行到下一个阶段,或者步骤。而在每一个步骤里,我们需要决策到底是从左上过来,还是从右上过来。在运用动态规划方法分析问题的过程中,下面四个要素是要明确的:

  1. 状态。状态用于描述每一个步骤的参数以及结果。在数字金字塔的例子中,每个f[i][j]表示的就是一个状态。其中数组下标是当前路径的结尾,而值是以i行j列元素为结尾的所有路径中的最大值。
  2. 转移方程。转移方程用于描述不同状态之间的关系。在上面的例子中,f[i][j] = max(f[i - 1][j - 1], f[i - 1][j]) + a[i][j]就是一条转移方程。它描述了结尾为下一行的第j个结点的路径,和以上一行第j-1个结点和第j个结点路径之间的关系。
  3. 初始状态。初始状态描述的是整个转移方程推导的开始,是不需要经由别的状态就知道结果的状态。上面的例子中,f[1][1]=a[i][j]就是初始状态。我们以这个状态为起点,最终推导出整个三角形上每一个位置的答案。
  4. 转移方向。转移方向描述的是推导出不同状态的解的先后关系。我们之所以要明确转移方向,是因为我们不希望"已知B状态只能由A状态推到过来。但是当我们想推导B时,发现A状态的结果我们还不知道”类似的事情发生。比如由转移方程中f[i][j] = max(f[i - 1][j - 1], f[i - 1][j]) + a[i][j],我们发现,如果想推导f[i][j],必须先推导f[i - 1][j - 1]和f[i - 1][j]。所以,按照i从小到大,j从小到大的顺序推导是一种可行的推导方向。

所以,为了用动态规划解决问题,我们就需要明确上面四个方面,其中最重要的就是设计状态和转移方程。

动态规划条件
在这里指出,用动态规划求解要求我们设计出状态和转移方程,使得它们满足下面三个条件:
在这里插入图片描述
在这里插入图片描述
动态规划算法的关键在于解决冗余 ,这是动态规划算法的根本目的。

动态规划实质上是一种以空间换时间的技术,它在实现的过程中,不得不存储产生过程中的各种状态,所以它的空间复杂度要大于其他的算法。

选择动态规划算法是因为动态规划算法在空间上可以承受,而搜索算法在时间上却无法承受,所以我们舍空间而取时间。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

leetcode重点题目分类别记录(三)动态规划深入与素数理论

文章目录动态规划背包问题01背包抽象出求解目标尝试进程子问题拆分基本情况根据拆分过程定义dp数组与转移方程遍历顺序与状态压缩模板归纳题目应用变种提升组合问题多维01背包有特殊限制的01背包完全背包尝试进行子问题拆分转移方程题目应用变种提升-求组合/排列数打家劫舍变种…

二维数组的总结

一、时间复杂度和空间复杂度 时间复杂度和空间复杂度是衡量算法效率的两个重要指标。时间复杂度是指算法执行所需的时间&#xff0c;而空间复杂度是指算法执行所需的内存空间。 计算时间复杂度和空间复杂度需要分析算法中各个操作的执行次数和内存使用情况。具体的计算方法可以…

【matlab代码】提取任意多边形内的nc数据--以海洋温度为例子

【matlab代码】提取任意多边形内的nc数据–以海洋温度为例子 本文来源于对象想提取一个矩形内的温度数据,从而求平均能看出时间序列变化。 由于平时我们矩形是和经纬度平行,我们可以直接使用lon,lat进行寻找。 例子如图: 图片 这样的水平图,大家都会,直接: find(lon…

数据库作业合集

目录[TOC](目录)数据库作业第七章&#xff08;1&#xff09;习题7关系模式&#xff08;2&#xff09;习题8关系模式数据库作第三章34数据库作第二章6数据库作业第三章59数据库作业第八章&#xff08;1&#xff09;统计离散数学的成绩分布情况&#xff0c;即按照各分数段统计人数…

ROS--URDF集成Gazebo仿真小车和rviz结合

ROS–URDF集成Gazebo仿真小车 实现流程: 需要编写封装惯性矩阵算法的 xacro 文件为机器人模型中的每一个 link 添加 collision 和 inertial 标签&#xff0c;并且重置颜色属性在 launch 文件中启动 gazebo 并添加机器人模型工作目录 1.编写封装惯性矩阵算法的 head.xacro 文件…

【MySQL】B+树索引——InnoDB 中的索引方案;MylSAM 中的索引方案 和 InnoDB 中的索引方案 对比

一、InnoDB 中的索引方案 1. 聚簇索引 聚簇索引 有两个特点&#xff1a; 使用记录主键值的大小进行记录和页的排序&#xff0c;这包括3方面的含义. &#xff08;1&#xff09;页〈包括叶子节点和内节点〉内的记录按照主键的大小顺序排成一个单向链表&#xff0c;页内的记录被…

MongoDB 介绍和基本操作

一、MongoDB数据库 1、MongoDB是一种非关系型数据库&#xff0c;是用C语言编写的。其特点是高性能、易部署、易使用&#xff0c;存储数据方便。 2、MongoDB特点&#xff1a; 面向集合存储&#xff0c;易于存储对象类型数据&#xff1b;支持动态查询&#xff0c;支持完全索引&…

灌区量测水系统

1)灌区量测水 灌区量测水是水资源管理的基础&#xff0c;是推进节水农业和水价改革的重要手段。常规在主要水闸处&#xff0c;监测闸前和闸后水位及闸门开启状态(闸位)&#xff0c;通过实时监测数据&#xff0c;计算过闸流量。要实现全灌区水资源动态配置、精准灌溉&#xff0…

C语言小项目 -- 通讯录完整代码(登陆系统+动态开辟 + 文件操作)

目录 &#x1f4f0;0. 项目介绍 &#x1f4f0;1. 开发环境及框架 &#x1f4f0;2. 通讯录账户模块功能分析实现&#xff1a; &#x1f4f1;2.1 通讯录账户菜单界面及数据结构设计 &#x1f4f1;2.2 通讯录账户注册功能实现 &#x1f4f1;2.3 通讯录账户登录功能实现 &am…

完美解决丨 - [SyntaxError: invalid syntax](#SyntaxError-invalid-syntax)

目录 报错名称SyntaxError: invalid syntaxNameError: name xx is not definedIndentationError: expected an indented blockAttributeError: xx object has no attribute xxTypeError: xx object is not callableValueError: I/O operation on closed fileOSError: [Errno 2]…

目前的Android 市场怎么样?还好吗?

如今&#xff0c;随着互联网和移动设备的普及&#xff0c;Android 系统已成为全球最大的移动操作系统之一&#xff0c;成为最受欢迎的应用程序开发平台之一。作为一名 Android 开发者&#xff0c;我们生活中的大部分应用程序都是基于 Android 平台开发的&#xff0c;而我们的工…

从字节码分析String创建的几种方式

一.String a new String("a"); 1.到底会不会进入常量池 String a new String("a"); 通过idea中jclasslib插件获取到字节码 0 new #2 3 dup 4 ldc #3 <a> 6 invokespecial #4 <java/lang/String.<init> : (Ljava/lang/String;)V>9 as…

MongoDB 聚合管道中使用数组表达式运算符获取数组长度($size)和反转数组($reverseArray)

数组表达式运算符主要用于文档中数组的操作&#xff0c;本篇我们主要介绍如何使用数组表达式运算符获取数组的长度以及对数组中的数据进行反转&#xff1a; 一、准备数据 初始化成员数据 db.persons.insertMany([{ "_id" : "1001", "name" : …

go错误处理

func test() {num1 : 10num2 : 0result : num1 / num2fmt.Println("result", result)} func main() {test()for {fmt.Println("运行完毕&#xff01; main 下面的代码")time.Sleep(time.Second)}}在默认情况下&#xff0c;当发生错误后(panic) ,程序就会…

港联证券|揭秘涨停 旅游板块掀涨停潮

今天&#xff0c;A股三大股指低开低走。沪深两市收盘共38股涨停。剔除7只ST股&#xff0c;合计31股涨停。另外&#xff0c;14股封板未遂&#xff0c;整体封板率为73.08%。 涨停战场&#xff1a;6股封单资金超亿元 港联证券核算&#xff0c;从收盘涨停板封单量来看&#xff0c;…

【Pytorch】数据预处理

Pytorch是机器学习里面常用的框架之一&#xff0c;我们在学习机器学习之前最好需要学习如何使用这个框架对我们将要使用的数据数据进行预处理操作。 如果我们想要学习好pytorch里面的方法&#xff0c;我们需要常去用一下dir()和help()函数&#xff0c;它们一个会帮我们查看某个…

计算机软考考什么?怎么备考啊?

计算机软考是国家承认的计算机职业资格考试&#xff0c;是计算机行业从业者晋升职业等级的重要途径。计算机软考分为三个等级&#xff0c;分别是&#xff1a;初级、中级和高级。 备考计算机软考需要全面准备&#xff0c;下面我将从如何选择考试科目、如何制定学习计划、如何进…

【STL系列】unordered_set和unordered_map

前言 之前&#xff0c;我们介绍了STL中树形结构容器:set、map、multiset、multimap。 在C98中&#xff0c;STL提供了底层为红黑树结构的一系列关联式容器&#xff0c;在查询时的效率可达到O(logN)&#xff0c;即最差情况下需要比较红黑树的高度次&#xff0c;但当树中的结点非…

九龙证券|昨夜,大涨!蔚来5.99%,小鹏15.22%,理想6.39%

当地时间周一&#xff0c;美股三大指数低开高走&#xff0c;尾盘小幅收涨。盘面上&#xff0c;银行股、航空股遍及上涨。 展望本周&#xff0c;包括美联储理事沃勒、鲍曼等官员将迎来下月会议沉默期前的最终说话&#xff0c;投资者需关注其对经济和货币政策前景的看法。此外&am…

牛客网华为机考题库 C++

题目汇总HJ2 计算某字符出现次数HJ3 明明的随机数HJ4 字符串分隔HJ5 进制转换HJ6 质数因子HJ7 取近似值HJ8 合并表记录 哈希表HJ9 提取不重复的整数HJ10 字符个数统计HJ11 数字颠倒HJ12 字符串反转HJ13 句子逆序HJ14 字符串排序HJ15 求int型正整数在内存中存储时1的个数HJ16 购…