DP动态规划入门(数字三角形、破损的楼梯、安全序列)

news2024/12/27 12:30:34

一、动态规划(DP)简介

动态规划(Dynamic Programming,简称DP)是运筹学的一个分支,它是一种通过将复杂问题分解成多个重叠的子问题,并通过子问题的解来构建整个问题的解的算法。在动态规划中,有几个核心概念需要理解:

  1. 状态:状态通常表示为形如dp[i][j] = val的取值,其中i和j是用于描述和确定状态所需的变量(下标),而val则是该状态对应的值。
  2. 状态转移:状态转移描述了不同状态之间如何相互转化。这通常可以表示为一个数学表达式,而转移的方向则决定了算法是迭代还是递归地进行。
  3. 最终状态:最终状态即题目所要求解的状态,也是我们通过动态规划算法最终要得到的答案。

动态规划的关键在于找到子问题之间的重叠关系,并存储这些子问题的解以避免重复计算。通过这种方式,动态规划能够在多项式时间内解决一些看似复杂的问题,如背包问题、最短路径问题等。在实际应用中,动态规划被广泛用于优化和控制问题,以及计算机视觉、生物信息学等领域。

二、动态规划的解题步骤

步骤一:确定状态

首先,需要明确问题的状态表示。在动态规划问题中,状态通常定义为“到第i个为止,xx为j(xx为k)的方案数/最小代价/最大价值”等。这里,“i”、“j”和可能的“k”是状态的参数,它们根据具体问题而定。状态的确切定义取决于问题的性质和所需优化的目标(如最小代价、最大价值或方案数)。

步骤二:确定状态转移方程

状态转移方程是动态规划的核心,确定状态转移方程,即从已知状态得到新状态的方法,并确保按照这个方向一定可以正确地得到最终状态。根据状态转移的方向来决定使用迭代法还是递归法(记忆化)。状态转移方程的确立通常基于问题的特定条件和约束。

步骤三:确定最终状态并输出

最终状态通常是问题的解所对应的状态。在确定了状态转移方程后,我们需要按照这个方程迭代或递归地计算出所有可能的状态,直到达到最终状态。最终状态可能是通过迭代法逐步累积得到,也可能是通过递归法(结合记忆化以避免重复计算)逐步回溯得到。一旦到达最终状态,我们就可以根据问题的要求输出相应的解,如最小代价、最大价值或特定条件下的方案数。

综上所述,这三个步骤——确定状态、确定状态转移方程和确定最终状态并输出——构成了动态规划求解问题的一般框架。在实际应用中,根据具体问题的不同,这些步骤的具体实现方式也会有所不同。

三、线性DP例题

(一)数字三角形(最大路径)

上图给出了一个数字三角形。从三角形的顶部到底部有很多条不同的路径对于每条路径,把路径上面的数加起来可以得到一个和,你的任务就是找到最大的和(路径上的每一步只可沿左斜线向下或右斜线向下走)。
输入描述
输入的第一行包含一个整数 N(1 ≤ N < 100),表示三角形的行数。
下面的 N 行给出数字三角形。数字三角形上的数都是 0 至 99 之间的整数。
输出描述
输出一个整数,表示答案。
输入样例

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

输出样例

30

思路:

  • 从最后一层向上,取最大值累加。
  • 确定状态:设状态dp[ i ][ j ] = a[ i ][ j ] + max(dp[ i + 1 ][ j ], dp[ i + 1 ][ j + 1 ]);
  • 状态转移方程为dp[i][j]=max(dp[i+1]lj],dp[i +1][j + 1]) ;
  • 因为这里需要用下面的状态更新上面的,所以我们应该从下往上进行状态转移。最后输出dp[1][1]

#include<bits/stdc++.h>
using namespace std;
using ll = long long;
const int N = 105;
ll a[N][N], dp[N][N];

int main()
{
	int n; cin >> n;
	for (int i = 1; i <= n; ++i)
		for (int j = 1; j <= i; j++)
			cin >> a[i][j];
	for (int i = n; i >= 1; i--)
		for (int j = 1; j <= i; j++)
			dp[i][j] = a[i][j] + max(dp[i + 1][j], dp[i + 1][j + 1]);
	cout << dp[1][1] << '\n';
	return 0;
}

(二)破损的楼梯(方案数)

问题描述:
小蓝来到了一座楼梯前,楼梯共有N级台阶。从第0级台阶出发,小蓝每次可以迈上1级或2级台阶。但是,楼梯上的第a1级、第a2级、第a3级,以此类推,共M级台阶的台阶面已经坏了,不能踩。

小蓝想要到达楼梯的顶端,即第N级台阶,且不能踩到坏台阶。请问他有多少种到达顶端的方案数?由于方案数可能很大,请输出结果对10^9+7取模的值。

样例输入

6 1 3

样例输出

4

思路:

  • 确定状态:状态dp[ i ]表示走到第 i 级台阶的方案数。
  • 确定状态转移方程:在正常的楼梯上,我们可以从第i - 1级台阶或第i - 2级台阶走到第 i 级台阶,因此状态转移方程为dp[ i ] = dp[ i-1 ]+dp[ i - 2 ]。
  • 然而,如果第 i 级台阶是破损的,则不能走到该台阶,此时我们需要将dp[ i ]设为0。
  • 最后我们输出dp[ n ],表示走到第 n 级台阶的方案数
#include<bits/stdc++.h>
using namespace std;
const int p = 1e9 + 7;
int n, m;
int main()
{
	cin >> n >> m;
	vector<int>dp(n + 1, 1);int temp = 0;
	for (int i = 1; i <= m; i++)
	{
		cin >> temp;
		dp[temp] = 0;
	}
	for (int i = 2; i <= n; i++)
	{
		if (!dp[i])continue;
		dp[i] = (dp[i - 1] + dp[i - 2]) % p;
	}
	cout << dp[n] << '\n';
	
	return 0;
}

(三)安全序列(方案数)

问题描述

小蓝是工厂里的安全工程师,他负责在工厂里安全地放置危险品油桶。工厂的空位排列成一条直线,共有n个空位。小蓝需要按照特定的规则在这些空位上放置油桶:每两个油桶之间至少需要k个空位隔开。现在,小蓝想知道有多少种不同的放置方案可以满足这些条件。由于可能的方案数非常大,输出结果需要对10^9 + 7取模。

输入格式

输入包含两个正整数n和k,分别表示空位的数量和每两个油桶之间至少需要的空位数。

输出格式

输出一个整数,表示满足条件的放置方案数对10^9 + 7取模的结果。

样例输入

4 1

样例输出

6

说明

在样例中,有4个空位,每两个油桶之间至少需要1个空位。可能的放置方案有6种,分别是:0000(不放任何油桶),1000,0100,0010,0001和1001(其中1表示放置油桶,0表示不放)。注意,这里的方案数已经对10^9 + 7取模。

评测数据规模

对于所有评测数据,1 ≤ n ≤ 10^9,1 ≤ k ≤ n-1。

思路:

首先,我们定义dp[i]为在前i个空位中放置油桶的方案数。然后,我们需要计算前缀和数组prefix。对于每个位置i,prefix[i]表示从位置0到位置i为止的所有放置方案数的总和。

ll dp[N], prefix[N];

循环遍历判断每个位置之前没有足够的空间放置另一个油桶 。

如果当前位置减去k再减1小于1,则dp[ i ]为1。

否则,dp[ i ]的值等于前缀和数组在( i - 1 - k )位置的值。

if (i - k - 1 < 1)dp[i] = 1;
else dp[i] = prefix[i - 1 - k];

设状态dp[ i ]表示以位置i结尾的方案总数,状态转移方程:dp[i] = \sum_{j=1}^{i-k-1}dp[j]

但是直接去求和肯定会超时,所以我们需要利用前缀和来优化时间复杂度(注意取模)。

prefix[i] = (prefix[i - 1] + dp[i]) % p;

计算到当前位置为止,包括所有符合条件的放置方案数的总和。 

#include<bits/stdc++.h>
using namespace std;
using ll = long long;
const ll N = 1e6 + 9, p = 1e9 + 7;

ll dp[N], prefix[N];

int main()
{
	int n, k; cin >> n >> k;
	dp[0] = prefix[0] = 1;// 初始化dp和prefix数组的第0项为1,表示空序列的方案数为1
	for (int i = 1; i <= n; ++i)// 遍历1到n,计算每个位置的dp值和前缀和
	{
		if (i - k - 1 < 1)dp[i] = 1;
		// 如果当前位置减去k再减1小于1,则dp[i]为1
		// 说明在当前位置之前没有足够的空间放置另一个油桶,
		// 因此在这种情况下,只能选择不放置油桶,所以'dp[i]'被设为'1'。
		else dp[i] = prefix[i - 1 - k];
		// 否则,dp[i]的值等于前缀和数组在(i-1-k)位置的值
		// 这表示从位置'0'到位置'i-k-1'的所有放置方案数。
		// 这样做是因为每两个油桶之间需要至少'k'个空位,
		// 因此'dp[i]'实际上继承了在'i-k-1'位置及之前能放置油桶的所有方案。

		prefix[i] = (prefix[i - 1] + dp[i]) % p;
		// 计算到当前位置为止,包括所有符合条件的放置方案数的总和,并将结果模上'p'以防溢出。
	}
	cout << prefix[n] << '\n';

	return 0;
}

今天就先到这了!!!

看到这里了还不给博主扣个:
⛳️ 点赞☀️收藏 ⭐️ 关注!

你们的点赞就是博主更新最大的动力!
有问题可以评论或者私信呢秒回哦。

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

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

相关文章

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

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

.NET Core 服务实现监控可观测性最佳实践

前言 本次实践主要是介绍 .Net Core 服务通过无侵入的方式接入观测云进行全面的可观测。 环境信息 系统环境&#xff1a;Kubernetes编程语言&#xff1a;.NET Core ≥ 2.1日志框架&#xff1a;Serilog探针类型&#xff1a;ddtrace 接入方案 准备工作 DataKit 部署 DataK…

软件推荐 篇三十七:安卓软件推荐IP Tools「IP工具」:全面解析网络状态与管理的必备神器

引言&#xff1a; 随着互联网的普及&#xff0c;网络已经成为我们日常生活中不可或缺的一部分。无论是工作、学习还是娱乐&#xff0c;我们都需要通过网络来进行各种操作。然而&#xff0c;网络问题的出现往往会给我们带来诸多困扰。为了更好地管理和优化网络&#xff0c;我们…

V2X技术与智能传感器的完美融合:提升城市道路安全

在科技不断创新的今天&#xff0c;城市交通领域涌现了大量新技术。有时候我们不仅仅需要独立应用这些新技术来实现交通的变革&#xff0c;更需要将它们巧妙地结合连接起来&#xff0c;以获取更高效更安全的交通环境。本文将探讨V2X技术与智能传感器的结合&#xff0c;如何在城市…

如何使用 Elasticsearch 作为向量数据库

在今天的文章中&#xff0c;我们将很快地通过 Docker 来快速地设置 Elasticsearch 及 Kibana&#xff0c;并设置 Elasticsearch 为向量搜索。 拉取 Docker 镜像 docker pull docker.elastic.co/elasticsearch/elasticsearch:8.12.2 docker pull docker.elastic.co/kibana/kiba…

python网络相册设计与实现flask-django-nodejs-php

此系统设计主要采用的是python语言来进行开发&#xff0c;采用django框架技术&#xff0c;框架分为三层&#xff0c;分别是控制层Controller&#xff0c;业务处理层Service&#xff0c;持久层dao&#xff0c;能够采用多层次管理开发&#xff0c;对于各个模块设计制作有一定的安…

平衡隐私与效率,Partisia Blockchain 解锁数字安全新时代

原文&#xff1a;https://cointelegraph.com/news/exploring-multiparty-computations-role-in-the-future-of-blockchain-privacy&#xff1b; https://medium.com/partisia-blockchain/unlocking-tomorrow-outlook-for-mpc-in-2024-and-beyond-cb170e3ec567 编译&#xff1…

学习笔记Day14:Linux下软件安装

软件安装 Anaconda 所有语言的包(package)、依赖(dependency)和环境(environment)管理器&#xff0c;类似应用商店 Conda < Miniconda < Anaconda&#xff08;有交互界面&#xff09; Linux下Miniconda即可 安装Miniconda 搜索北外/清华miniconda镜像网站&#xff…

网络安全笔记-day6,NTFS安全权限

文章目录 NTFS安全权限常用文件系统文件安全权限打开文件安全属性修改文件安全权限1.取消父项继承权限2.添加用户访问权限3.修改用户权限4.验证文件权限5.总结权限 强制继承父项权限文件复制移动权限影响跨分区同分区 总结1.权限累加2.管理员最高权限2.管理员最高权限 NTFS安全…

利用CSS3实现正在加载效果

一、代码区域 1.1css3代码 <style>* {padding: 0;margin: 0;list-style: none;}.loading {width: 300px;height: 100px;margin: 100px auto;}.loading ul {height: 100px;width: 65px;margin: 0 auto;display: flex;align-items: center;}.loading ul li {margin: 0 5p…

么样才能用最便捷的方式为Mac提速呢?

Mac是现代人日常工作时必不可少的工具&#xff0c;尤其是在居家办公已经屡见不鲜的当下。视频会议、文档传送、视频剪辑等等。它在工作中扮演的角色越来越重要&#xff0c;所以也导致了它的流畅程度可以在很大程度上影响人们一整天的工作效率和心情。 但是影响Mac的运行和响应速…

Android - 深入浅出理解SeLinux

1. 概述 官方文档&#xff1a; https://source.android.com/docs/security/features/selinux https://source.android.com/docs/security/features/selinux/images/SELinux_Treble.pdf Your visual how-to guide for SELinux policy enforcement | Opensource.com SeLinux&…

计网--网络层个人笔记

网络层的IP分组由路由器转发&#xff0c;而每一个路由器有很多接口&#xff0c;那么从哪一个接口转发便需要转发表。 一、网络层基本功能 二、SDN基本概念 2.1 路由器 数据平面&#xff1a;转发的过程。一个分组如何从一个端口到达另一个端口。 控制平面&#xff1a;路由选择…

小程序渲染层图标错误

小程序渲染图标层出现错误&#xff1a; 官方提示&#xff1a;不影响可以忽略&#xff1b; 通过阿里巴巴矢量图标库--项目设置--字体格式--选中base64格式&#xff1b; 重新更新图标库代码&#xff0c;替换项目中的图标库&#xff1b; 重新加载小程序--渲染层错误的提示消失&…

基于vue+element+springboot+uniapp开发的智慧城管源码,java智慧城市管理综合执法系统源码

智慧城管源码&#xff0c;智慧执法&#xff0c;数字化城市管理综合执法系统源码 智慧城管系统充分利用物联网、云计算、信息融合、网络通讯、数据分析与挖掘等技术&#xff0c;对城市管理进行全方位覆盖。它通过建立城市综合管理平台&#xff0c;将城市的信息和管理资源有机结合…

TS + Vue3 elementUI 表格列表中如何方便的标识不同类型的内容,颜色区分 enum

TS Vue3 elementUI 表格列表中如何方便的标识不同类型的内容&#xff0c;颜色区分 enum 本文内容为 TypeScript 一、基础知识 在展示列表的时候&#xff0c;列表中的某个数据可能是一个类别&#xff0c;比如&#xff1a; enum EnumOrderStatus{"未受理" 1,"…

鸿蒙Harmony应用开发—ArkTS(@State装饰器:组件内状态)

State装饰的变量&#xff0c;或称为状态变量&#xff0c;一旦变量拥有了状态属性&#xff0c;就和自定义组件的渲染绑定起来。当状态改变时&#xff0c;UI会发生对应的渲染改变。 在状态变量相关装饰器中&#xff0c;State是最基础的&#xff0c;使变量拥有状态属性的装饰器&a…

分享ChatGPT超全面从基础到实战视频教程 - 带源码课件

分享ChatGPT超全面从基础到实战视频教程 - 带源码课件。本课程从ChatGPT的注册和使用讲起&#xff0c;分析了主流ChatGPT的应用场景&#xff0c;针对代码类开发场景做了详细实践&#xff0c;通过ChatGPT完成实战项目&#xff0c;从0-1搭建聊天机器人&#xff0c;轻松学习ChaGPT…

【mysql 127错误】mysql启动报错mysqld.service: Failed with result ‘exit-code‘.

无网环境&#xff0c;mysql 安装 出现如下错误 [rootmysql tools]# systemctl status mysqld.service ● mysqld.service - MySQL ServerLoaded: loaded (/usr/lib/systemd/system/mysqld.service; enabled; vendor preset: disabled)Active: failed (Result: exit-code) since…

基于OpenCV的图像处理案例之图像矫正(Python)

Index 目录索引 写在前面解决思路参考 写在前面 本文通过一个案例介绍如何使用OpenCV将倾斜的扫描文档图像进行水平矫正。 解决思路 因为扫描图像中的大部分文字倾斜后&#xff0c;同一行文字也在同一条直线&#xff0c;所以可以通过拟合直线来计算文本倾斜角度&#xff0c;…