C++ 动态规划

文章目录

  • 一、简介
  • 二、举个栗子
    • 2.1斐波那契数列
    • 2.2最短路径(DFS)
  • 参考资料

一、简介

感觉动态规划非常的实用,因此这里整理一下相关资料。动态规划(Dynamic Programming):简称 DP,是一种优化算法,它特别适合去优化一些问题,如最短路径等(设计到最小化以及最大化的问题,都可以考虑该方法),它具有通用性。通俗来讲,可以将其视为一种穷举搜索算法,但是不同于穷举算法,它会避免许多无意义的重复操作,从而节省时间,因此也可以将其描述为“谨慎的蛮力”。

tips:动态规划一词最早由理查德·贝尔曼于 1957 年在其著作《动态规划(Dynamic Programming)》一书中提出。这里的 Programming 并不是编程的意思,而是指一种[表格处理方法],即将每一步计算的结果存储在表格中,供随后的计算查询使用,据说是最早用于处理火车的规划问题。还有另一个原因就是,本来贝尔曼想以“研究(research)”之类的词进行命名,但是国防部的官员对“研究”一词极为恐惧和厌恶,因此就采用了Programming一词(折中方案)。

DP问题存在这样一个通用的框架:

  1. 记忆化处理(记录每次计算的结果)。
  2. 找出子问题(它往往与其他问题有所关联,其结果可以被重复使用,注:子问题的依赖关系应是非循环的)。
  3. 穷举所有可能的结果(也就是,如最短路径),有的算法不需要这一步处理。

因此DP问题也可以被描述为一个:递归+记忆化处理+猜(可能存在)的过程,它的计算时间是子问题数量*每个子问题所花费的时间。当然一句话的概况往往是有形而无用的,还是需要多结合实际情况去感受,因此可以以一些例子来进一步学习。

二、举个栗子

2.1斐波那契数列

1,1,2,3,5,8,13,21,34,55,89……

首先我们可以写一个原始的版本(递归):

#include <iostream>
#include <unordered_map>

int f(int n)
{
	if (n < 2)
		return 1;
	else
		return f(n - 1) + f(n - 2);
}

int main(int argc, char* argv[])
{
	// -------------------------动态规划---------------------------
	// 斐波那契数列
	int n = 7;		//以0为起始
	std::cout << "计算结果:" << f(n) << std::endl;
	
	std::cout << "计算结束!" << std::endl;
	return 0;
}

在这里插入图片描述

不过由于上述的版本存在很多重复的计算,比如计算f(n)是会计算f(n-1)与f(n-2),而计算f(n-1)时则又会重新计算f(n-2),以此类推当n很大时,上面程序的复杂度会以指数级增长,因此这里就可以利用简单的动态规划思路来加速计算过程(有时候追本溯源还是很有用的,我们只需要像创始人那样创建一个表即可)。

#include <iostream>
#include <unordered_map>

//创建一个表用于记录
std::unordered_map<int, int> fm;

int f(int n)
{
	if (fm.find(n) != fm.end())
		return fm[n];

	if (n < 2)
	{
		fm[n] = 1;
		return 1;
	}
	else
	{
		fm[n] = f(n - 1) + f(n - 2);
		return fm[n];
	}
}

int main(int argc, char* argv[])
{
	// -------------------------动态规划---------------------------
	// 斐波那契数列
	int n = 7;		//以0为起始
	std::cout << "计算结果:" << f(n) << std::endl;
	
	std::cout << "计算结束!" << std::endl;
	return 0;
}

在这里插入图片描述

不过上述的代码仍然不够完美,这是因为我们是自顶向下的过程,这个过程中我们依赖于递归这种方式,存在许多函数调用的过程,因此我们可以继续简化:

#include <iostream>
#include <unordered_map>

int main(int argc, char* argv[])
{
	// -------------------------动态规划---------------------------
	// 斐波那契数列
	int n = 7;		//以0为起始

	std::unordered_map<int, int> f;
	for (int i = 0; i <= n; ++i)
	{
		if (i < 2)
			f[i] = 1;
		else
			f[i] = f[i - 1] + f[i - 2];
	}
	std::cout << "计算结果:" << f[n] << std::endl;

	std::cout << "计算结束!" << std::endl;
	return 0;
}

在这里插入图片描述

2.2最短路径(DFS)

假设从一个棋盘的左上角走到右下角,求取最大路径之和,思路其实和上面相同,只是操作上略有不同:

// 标准文件
#include <iostream>
#include <thread>
#include <chrono>
#include <vector>
#include <fstream>
#include <stack>

#define COMP >

static int maxPathSum(std::vector<std::vector<int>>& grid) {
    int b = grid[0].size();
    int c = grid.size();

    std::vector<std::vector<float>> dp(c);
    std::vector<std::vector<std::pair<int, int>>> coords(c);
    for (int i = 0; i < dp.size(); ++i)
    {
        dp[i].resize(b);
        coords[i].resize(b);
    }
    //int dp[c][b];
    std::cout << "行数:" << c << ",列数:" << b << std::endl;
    dp[0][0] = grid[0][0];
    coords[0][0] = std::make_pair(-1, -1);

    //初始化行
    for (int i = 1; i < c; i++)
    {
        dp[i][0] = dp[i - 1][0] + grid[i][0];
        coords[i][0] = std::make_pair(i - 1, 0);
    }

    //初始化列
    for (int j = 1; j < b; j++)
    {
        dp[0][j] = dp[0][j - 1] + grid[0][j];
        coords[0][j] = std::make_pair(0, j - 1);
    }

    for (int i = 1; i < c; i++)
    {
        for (int j = 1; j < b; j++)
        {
            if (dp[i - 1][j] COMP dp[i][j - 1]
                && dp[i - 1][j] COMP dp[i - 1][j - 1])
            {
                coords[i][j] = std::make_pair(i - 1, j);
                dp[i][j] = dp[i - 1][j] + grid[i][j];
            }
            else if (dp[i - 1][j - 1] COMP dp[i][j - 1]
                && dp[i - 1][j - 1] COMP dp[i - 1][j])
            {
                coords[i][j] = std::make_pair(i - 1, j - 1);
                dp[i][j] = dp[i - 1][j - 1] + grid[i][j];
            }
            else
            {
                coords[i][j] = std::make_pair(i, j - 1);
                dp[i][j] = dp[i][j - 1] + grid[i][j];
            }

            //dp[i][j] = std::max(dp[i - 1][j],
            //    std::max(dp[i - 1][j - 1], dp[i][j - 1]))
            //    + grid[i][j];
        }
    }

    //距离矩阵
    std::cout << "距离矩阵:" << std::endl;
    for (int i = 0; i < dp.size(); ++i)
    {
        std::vector<float> row = dp[i];
        for (int j = 0; j < row.size(); ++j)
        {
            std::cout << row[j] << " ";
        }
        std::cout << std::endl;
    }

    //索引矩阵
    std::cout << "索引矩阵:" << std::endl;
    for (int i = 0; i < coords.size(); ++i)
    {
        std::vector<std::pair<int, int>> row = coords[i];
        for (int j = 0; j < row.size(); ++j)
        {
            std::cout << "(" << row[j].first << "," << row[j].second << ")" << " ";
        }
        std::cout << std::endl;
    }

    std::cout << "输出路径:" << std::endl;
    std::deque<std::pair<int, int>> queue;
    queue.push_front(std::make_pair(c - 1, b - 1));
    std::pair<int, int> pos = coords[c - 1][b - 1];
    while (pos.first > -1)
    {
        queue.push_front(pos);
        pos = coords[pos.first][pos.second];
    }
    for (int i = 0; i < queue.size() - 1; ++i)
    {
        std::cout << "(" << queue[i].first << "," << queue[i].second << ")" << "->";
    }
    std::cout << "(" << queue[queue.size() - 1].first << ","
        << queue[queue.size() - 1].second << ")" << "\n";

    return dp[grid.size() - 1][grid[0].size() - 1];
}

int main(int argc, char** argv)
{
    // ---------------------输入数据---------------------
    std::vector<std::vector<int>> data =
    {
        {1,3,1,1},
        {1,5,1,1},
        {4,2,1,1}
    };

    // ---------------------动态规划---------------------
    std::cout << "最大距离:" << maxPathSum(data) << std::endl;

    return 0;
}

在这里插入图片描述

参考资料

[1]https://leetcode.com/problems/minimum-path-sum/description/
[2]https://www.youtube.com/watch?v=OQ5jsbhAv_M

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

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

相关文章

RHCE:请给openlab搭建web

1.关闭所有安全软件已经防火墙 2.安装所需软件 3.在Windows 文件中进行DNS映射 C:\Windows\System32\drivers\etc\hosts 文件进 行DNS 映射 4.创建www.openlab.com网站 5.创建教学资料子网站 6.创建学生信息子网站 进行验证 7.创建缴费子网站

【LLM多模态】Cogvlm图生文模型结构和训练流程

note Cogvlm的亮点&#xff1a; 当前主流的浅层对齐方法不佳在于视觉和语言信息之间缺乏深度融合&#xff0c;而cogvlm在attention和FFN layers引入一个可训练的视觉专家模块&#xff0c;将图像特征与文本特征分别处理&#xff0c;并在每一层中使用新的QKV矩阵和MLP层。通过引…

直接选择排序(六大排序)

1. 选择排序基本思想&#xff1a; 每一次从待排序的数据元素中选出最小&#xff08;或最大&#xff09;的一个元素&#xff0c;存放在序列的起始位置&#xff0c;直到全部待排序的数据元素排完 。 选择排序包含&#xff1a;1.直接选择排序 2.堆排序 2 直接选择排序: ●在元素…

如何快速下载GEO数据并获取其表达矩阵与临床信息 | 附完整代码 + 注释

GEO数据库可以说是大家使用频率贼高的数据库啦&#xff01;那它里面的数据怎么下载大家知道嘛&#xff01;今天给大家展示一种快速获取它的表达矩阵和临床信息的方法&#xff01; 话不多说&#xff01;咱们直接开始&#xff01; GEO编号获取 在GEO数据库中&#xff0c;你找到…

基于springboot善筹网(众筹)前后台实现设计(带源码)

信息数据从传统到当代&#xff0c;是一直在变革当中&#xff0c;突如其来的互联网让传统的信息管理看到了革命性的曙光&#xff0c;因为传统信息管理从时效性&#xff0c;还是安全性&#xff0c;还是可操作性等各个方面来讲&#xff0c;遇到了互联网时代才发现能补上自古以来的…

36.网络游戏逆向分析与漏洞攻防-游戏网络通信数据解析-数据解码器的实现

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 如果看不懂、不知道现在做的什么&#xff0c;那就跟着做完看效果 内容参考于&#xff1a;易道云信息技术研究院VIP课 上一个内容&#xff1a;35.登录成功数据…

Oracle 19cADG集群补丁升级

Oracle 19cADG集群补丁升级 文章目录 Oracle 19cADG集群补丁升级1.备库备份2.备库升级Opatch3.备库应用补丁4.主库备份 oracle_home目录5.主库升级Opatch6.注册补丁7.编译无效对象8.检查主库的补丁注册情况9.备库切换主库完成补丁注册 1.备库备份 su - oracle cd $ORACLE_HOME…

3、创建项目,什么是路由

一、创建项目 第一次全局安装脚手架 npm install -g vue/clivue create 项目名 二、什么是路由&#xff1f; 路由就是一组 key-value 的对应关系多个路由&#xff0c;需要经过路由器的管理 1、后端路由&#xff1a; 每个url地址都对应着不同的静态资源对于普通的网站。所有…

【Godot4自学手册】第二十九节使用Shader来实现敌人受伤的闪白效果

在Godot 4中&#xff0c;Shader是用来为材质提供自定义渲染效果的程序。材质可以应用于MeshInstance、CanvasItem和ParticleEmitter等节点。Shader可以影响顶点的变换、片段&#xff08;像素&#xff09;的颜色&#xff0c;以及光照与物体的交互。 在Godot中&#xff0c;Shader…

ROS 2边学边练(1)-- 安装Iron Irwini

其实是从去年的一个机缘巧合才开始了解到ROS&#xff0c;但也仅限于此了&#xff08;看了古月居的入门21讲&#xff09;&#xff0c;今年买了几本关于ROS相关的书籍&#xff0c;比如《精通ROS机器人编程 Lentin Joseph&Jonathan Cacace》、《ROS 2机器人编程实战-基于现代C…

AI入侵游戏业:是颠覆者还是创新助手?揭秘未来游戏新趋势!

在科技日新月异的今天&#xff0c;人工智能&#xff08;AI&#xff09;已经成为各行各业的关注焦点。而在娱乐产业中&#xff0c;AI技术的引入也让人们对电子游戏的未来发展产生了无限遐想。那么&#xff0c;AI究竟会给电子游戏行业带来怎样的变革&#xff1f;它会成为行业的颠…

【消息队列开发】 实现BrokerServer类——本体服务器

文章目录 &#x1f343;前言&#x1f38b;创建 BrokerServer 类&#x1f38d;启动与停止服务器&#x1f340;实现处理连接&#x1f384;实现 readRequest 与 writeResponse&#x1f334;实现处理请求&#x1f332;实现 clearClosedSession⭕总结 &#x1f343;前言 本次开发任…

java的ArrayList类

ArrayList<E>E是自定义数据类型 ArrayList类&#xff1a; 构造函数&#xff1a; 成员方法&#xff1a; public boolean add(E e)&#xff1a; 将指定元素加到集合末尾 Appends the specified element to the end of this list. public class Array {public static…

就业班 第二阶段 2401--3.26 day6 Shell初识 连接vscode

远程连接vs_code可能出现的问题 C:\Users\41703\.ssh 验证远程主机的身份&#xff0c;如果连不上vscode&#xff0c;可以尝试删除这里面的公钥代码。 重新安装那个扩展&#xff0c;排除扩展本身的问题 谁连过我&#xff0c;并操作了什么 curl https://gitea.beyourself.org.c…

【软件测试】功能测试/接口测试/自动化测试/性能测试/验收测试

软件测试的主要流程 一、测试主要的四个阶段 1.测试计划设计阶段&#xff1a;产品立项之后&#xff0c;进行需求分析&#xff0c;需求评审&#xff0c;业务需求评级&#xff0c;绘制业务流程图。确定测试负责人&#xff0c;开始制定测试计划&#xff1b; 2.测试准备阶段&…

C++11与thread相关使用(纯代码)

多线程创建 //多线程创建 void print(string s) {cout << "i am a new thread&#xff1a;" << s << endl; } int main() {//move将左值变成右值(右值引用过后的属性是左值)//thread t1(print, "t1");//thread t2(move(t1));//调用移动…

javaWeb校园二手平台项目

一、系统分析 1.1开发背景 随着全世界互联网技术的不断发展&#xff0c;各种基于互联网技术的网络应用不断涌现,网络技术正在不断的深入人们的生活。人们从Internet上获取信息、享受生活、交流感情、网上工作等。Internet正在迅速改变着人们的生活方式。 经过我国改革开放多年…

CI/CD实战-jenkins流水线 6

现最新版本没有该问题的出现 基于RBAC的身份授权&#xff1a; 安装插件&#xff1a; 新建测试用户 修改默认授权策略 新建的用户就没有任何权限 新建角色并授权 添加用户角色身份 pipeline 安装ssh agent插件 由于最新版的插件是有问题的&#xff0c;会有以下报错&#xff…

「媒体宣传」财经类媒体邀约资源有哪些?-51媒体

传媒如春雨&#xff0c;润物细无声&#xff0c;大家好&#xff0c;我是51媒体网胡老师。 财经类媒体邀约资源包括但不限于以下几类&#xff1a; 商业杂志和报纸&#xff1a;可以邀请如《财经》、《新财富》、《经济观察报》等主流商业杂志和报纸。这些媒体通常具有较强的品牌影…

自信当众讲话:从紧张到自如的转变之路

自信当众讲话&#xff1a;从紧张到自如的转变之路 在人生的舞台上&#xff0c;当众讲话是每个人都可能面对的挑战。然而&#xff0c;对于许多人来说&#xff0c;站在众人面前讲话却是一件令人紧张甚至恐惧的事情。这种紧张感往往源于对自我能力的怀疑&#xff0c;对未知的恐惧…