C++ 动态规划

news2024/9/20 7:47:00

文章目录

  • 一、简介
  • 二、举个栗子
    • 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

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

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

相关文章

后端常问面经之Java基础

基本数据类型 Java中有8种基本数据类型&#xff1a; 6种数字类型&#xff1a; 4种整数型&#xff1a;byte、short、int、long 2种浮点型&#xff1a;float、double 1种字符类型&#xff1a;char 1种布尔类型&#xff1a;boolean 数据类型的默认值以及所占空间如下&#x…

C++ STL - 优先级队列及其模拟实现

目录 0. 引言 1. priority_queue 介绍 1.1 构造函数 1.2 priority_queue 接口函数使用 1.3 仿函数 1.4 题目练习 2. priority_queue 模拟实现 2.1基本框架&#xff1a; 2.2 默认构造函数 2.3 基本函数 2.4 堆的向上以及向下调整 0. 引言 优先队列 (priority_queu…

Flask python :logging日志功能使用

logging日志的使用 一、了解flask日志1.1、Loggers记录器1.2、Handlers 处理器1.3、Formatters 格式化器 二、使用日志2.1、官网上的一个简单的示例2.2、基本配置2.3、具体使用示例2.4、运行 三、写在最后 一、了解flask日志 日志是一种非常重要的工具&#xff0c;可以帮助开发…

[技术杂谈]解决windows上出现文件名太长错误

最近执行python setup.py install总是失败&#xff0c;提示文件名太长发现网上有取消限制文件名长度&#xff0c;测试发现改完注册表无需重启cmd就生效了。但是有时候会失败&#xff0c;现在方法放这。 转到Windows“开始”&#xff0c;然后键入REGEDIT。选择注册表编辑器 选…

Linux内核中的进程调度-进程调度基础

前言 一、进程的概念 1.概述 2.ps和top命令 3.总结 二、进程的生命周期 1.进程状态文字描述 2.进程状态程序中的体现 3.进程状态的切换 三、task_struct数据结构简述 1.数据结构成员简述 2.需要注意的成员&#xff1a; 3.进程优先级 ①、优先级的代码表示 ②、Linux内核下的进…

蓝桥杯2023真题-幸运数字

目录 进制转换&#xff1a; 思路 代码 题目链接&#xff1a; 0幸运数字 - 蓝桥云课 (lanqiao.cn) 本题就考的进制转换问题&#xff0c;要将十进制5转换成二进制&#xff0c;通过%2,和/2的交替使用即可完成&#xff0c;所得余数就是转换成的二进制各位的值&#xff0c;转换…

浅谈如何自我实现一个消息队列服务器(3)—— 细节分析

文章目录 2.2 消息存储在文件时涉及到的流对象2.3 序列化、反序列化的方法2.3.1 JSON的ObjectMapper2.3.2 ObjectOutputStream 、 ObjectInputStream2.3.3 第三方库的Hessian2.3.4 protobuffer2.3.5 thrift 2.4 使用类MessageFileManager封装文件存储操作2.4.1 sendMessage()实…

ubuntu20.04云服务器安装LXDE轻量级桌面和XRDP远程连接工具

云服务器一般都是安装命令行系统&#xff0c;用SSH连接&#xff0c;但是有时我们需要桌面来做更好的管理。 首先我们明确一下需要的东西。 一个桌面系统&#xff1a;LXDE&#xff08;最轻量级桌面&#xff09;&#xff0c;为了节省资源&#xff0c;我们只要功能够用就行。一个…

[套路] 浏览器引入Vue.js场景-WangEditor富文本编辑器的使用 (永久免费)

系列文章目录 [套路] el-table 多选属性实现单选效果[套路] 基于服务内存实现的中文拼音混合查询[套路] Bypass滑块验证码 目录 系列文章目录前言一、实现1.1 场景1.2 Window对象简介1.3 引入WangEditor1.4 页面配置 前言 公司使用freemarker的老旧SpringBootWeb后台项目, 前…

【蓝桥杯】填空题技巧|巧用编译器|用Python处理大数和字符|心算手数|思维题

目录 一、填空题 1.巧用编译器 2.巧用Excel 3. 用Python处理大数 4.用Python处理字符 5.心算手数 二、思维题 推荐 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。【点击跳转到网站】 一、填空题 …

Python编程入门:环境搭建与基础语法

目录 1. 引言 2. Python环境搭建 3. Python基础语法 3.1. 变量与数据类型 3.2. 运算符与表达式 3.3. 控制结构&#xff1a;条件语句与循环 3.4. 函数定义与使用 3.5. 输入与输出 3.6. 列表操作 4. 总结 1. 引言 Python作为一种简洁易学、功能强大的编程语言&#xff…

hadoop伪分布式环境启动时web端访问不到

在搭建hadoop伪分布式环境时&#xff0c;开启hdfs-site.sh后&#xff0c;web端访问不到&#xff0c;但是节点已经正常开启&#xff1a; 在尝试关闭防火墙后也没有效果&#xff0c;后来在/etc/hosts文件中加入本机的ip和主机名映射后&#xff0c;重新初始化namenode&#xff0c;…

电脑桌面记事本便签软件,记事本软件哪个好用

正在电脑前忙碌工作&#xff0c;突然想起今晚有个重要的会议&#xff0c;或者是明天有一个重要的任务需要完成&#xff0c;但是手头的工作又无法让你离开电脑&#xff0c;这时候&#xff0c;你多么希望有一个便捷的电脑桌面记事本便签软件&#xff0c;可以让你快速记录下这些重…

2016年认证杯SPSSPRO杯数学建模D题(第二阶段)NBA是否有必要设立四分线全过程文档及程序

2016年认证杯SPSSPRO杯数学建模 D题 NBA是否有必要设立四分线 原题再现&#xff1a; NBA 联盟从 1946 年成立到今天&#xff0c;一路上经历过无数次规则上的变迁。有顺应民意、皆大欢喜的&#xff0c;比如 1973 年在技术统计中增加了抢断和盖帽数据&#xff1b;有应运而生、力…

软件测试/测试开发丨Docker环境安装配置(Mac、Windows、Ubuntu)

macOS 安装 Docker brew cask install docker运行 Docker Ubuntu 安装 Docker # 更新 apt update # 安装依赖 apt install apt-transport-https ca-certificates curl software-properties-common -y # 添加 key curl -fsSL https://mirrors.aliyun.com/docker-ce/linux/…

python 读取jpg图片

pillow读取图片 from PIL import Image import numpy as np img_path ./Training/meningioma/M546.jpg # 读取图片 image Image.open(img_path) width, height image.size print("图片的宽度为{},高度为{}".format(width,height)) print("图片的mode为{}&qu…

如何做到无感刷新Token?

为什么需要无感刷新Token&#xff1f; 自动刷新token 前端token续约 疑问及思考 图片 为什么需要无感刷新Token&#xff1f; 「最近浏览到一个文章里面的提问&#xff0c;是这样的&#xff1a;」 当我在系统页面上做业务操作的时候会出现突然闪退的情况&#xff0c;然后跳转…

vulhub打靶记录——cybox

文章目录 主机发现端口扫描web渗透nikto扫描目录扫描 提权 主机发现 使用nmap扫描局域网内存活的主机&#xff0c;命令如下&#xff1a; nmap -sP 192.168.56.0/24192.168.56.1&#xff1a;主机IP&#xff1b;192.168.56.100&#xff1a;DHCP服务器IP&#xff1b;192.168.56.…

通科技新品亮相:4K60编解一体,USB透传无忧

在信息化快速发展的今天&#xff0c;音视频技术的需求与应用场景日益丰富&#xff0c;特别是在对视频画质和实时性要求极高的领域中&#xff0c;如军警、公安、金融等&#xff0c;对音视频处理设备的性能要求更为严格。为满足这些高端应用场景的需求&#xff0c;视通科技紧跟时…

2024年【道路运输企业安全生产管理人员】考试及道路运输企业安全生产管理人员考试技巧

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 道路运输企业安全生产管理人员考试是安全生产模拟考试一点通总题库中生成的一套道路运输企业安全生产管理人员考试技巧&#xff0c;安全生产模拟考试一点通上道路运输企业安全生产管理人员作业手机同步练习。2024年【…