★动态规划(DP算法)详解

article/2025/9/13 9:04:30

什么是动态规划:动态规划_百度百科

内容太多了不作介绍,重点部分是无后效性,重叠子问题,最优子结构。

d6eac15165f240f8999e08b97665cf8f.png问S->P1和S->P2有多少种路径数,毫无疑问可以先从S开始深搜两次,S->P1和S->P2找出所有路径数,但是当这个图足够大,那就会超时。

动态规划旨在用空间换时间,我们可以发现S->P2的路上,实际上重复计算了S->P1,然后再去计算P1->P2,如果我们第一次计算S->P1的时候,保留了P1点的路径数,那么就不用再次计算S->P1了。

无后效性:未来的状态不会影响过去的状态,如果我在P1->P2的时候,S->P1多了一条路出来,那么先前保留的路径数就是错误的。

tip:下面的例题讲解并不是特别好,还未修正,建议先拉到最下面看20230504 Update.


经典的数塔问题也是dp算法的入门问题之一

假设你有这么一个数塔,你的目标是求最底层到最高层,求出最大路径和

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAenpj5aSn6a2U546L,size_20,color_FFFFFF,t_70,g_se,x_16

比如3->7->2->9这个路径,他的路径和是3+7+2+9

不难发现如果要求到9的最大路径和,首先要求出他前一层的最大路径

核心代码dp[i][j]=max(dp[i-1][j],dp[i-1][j+1])+a[i][j]

dp[i][j](9的最大路径和)

a[i][j](9自己)

dp[i-1][j](前一层5的最大路径和)dp[i-1][j+1](前一层2的最大路径和)

在前一层的最大路径和取大的那一个


例题:[NOIP2005 普及组] 采药 - 洛谷

e56aa3aa628848259cb937a08de76309.png

f843d45d8cde4de7b4ea4d60108828e2.png

这道题输入这么少也不用scanf了,直接上cin加上小优化

inline void scan(){ios::sync_with_stdio(false);//解除与scanf和cout的同步,具体体现在缓冲区cin.tie(nullptr),cout.tie(nullptr);//可以加快一点速度cin>>T>>m;for(int i=1;i<=m;++i)cin>>t[i]>>v[i];
}

很明显就是各个最优子问题的问题,要取到最多的价值,必然前一个状态也是最多的。

所以考虑定义问题的全部基础属性,每个草药有价值和对应所需的时间,目标是最大价值。

作出以下定义

定义dp[i][j]为采前i朵,消耗时间j内的最大价值不难得出以下两种情况
1.当前时间可以采走这支草药(1)如果要采这支采药,那先前状态就要预留j-t[i]时间来满足定义(2)如果不采那最大价值就是先前状态dp[i-1][j]因为不清楚哪一种情况更好,但是我们只需要最大值,所以max综上dp[i][j]=max(dp[i-1][j],dp[i-1][j-t[i]]+v[i]);2.当前时间不够采走这支草药,采不了那就不采,继承先前状态.  即dp[i][j]=d[i-1][j]或者说延续用上文的想法,n支草药价值最大我不知道
如果只有1支草药呢?,那我是不是就知道了
1支草药知道了,那我2支草药是不是也知道了

然后...直接从基础状态循环到结束,即从第一支草药开始循环。

显而易见不能从采最后几朵开始往前判断,前面状态都不清楚,怎么可以从后面开始。

AC代码

#include <bits/stdc++.h>
using namespace std;int T,m,t[101],v[101],dp[101][1001];
int main(){ios::sync_with_stdio(false);cin.tie(nullptr),cout.tie(nullptr);cin>>T>>m;for(int i=1;i<=m;++i)cin>>t[i]>>v[i];for(int i=1;i<=m;++i){for(int j=1;j<=T;++j){if(j>=t[i])dp[i][j]=max(dp[i-1][j],dp[i-1][j-t[i]]+v[i]);else dp[i][j]=dp[i-1][j];}}cout<<dp[m][T];return 0;
}

这道题还可以接着优化,因为不难发现dp[][]的一维空间始终是在dp[i]和dp[i-1]范围内
也就是说我们其实可以舍弃这一维来节约很大范围的空间。

dp代码

    for(int i=1;i<=m;++i)for(int j=T;j>=t[i];j--)dp[j]=max(dp[j-t[i]]+v[i],dp[j]);

内部循环必须从大到小,因为先前是应用了i-1先前状态去得出i状态,但是此处舍去了一维之后就会导致两者状态都会出现在这个小小的一维数组里面。

7f69dc1f7a434ace919173e82497844c.png

比如说用线段来表示所有情况,蓝色的得出了红色的状态。

但是如果我只剩下了一条线段呢?

b986bee879174438a3cae9d4298954ec.png

 如果中间被前面先修改了,那么后面要更新状态的时候用的就是红色,而不是我们所需要的蓝色。

所以只能从后往前去推状态才能保证我们所需要的一直是蓝色,而不会被更新成红色。


发现了吧,重点在于找出继承状态(递推式),比如定义的是前n个人,而不是任意n个人,这样n-1和n的区别就在于多了一个人,只要让先前状态抽出能满足多一个人的情况,那就是后者的状态。

例题:5 倍经验日 - 洛谷

b0782c7e2caf480cae6f7f3ecf7dd4ec.png

09856a1b036949339caa969f3486327e.png

 和上面那道题基本没有什么区别,定义所有相关的基础态

定义dp[i][j]为前i个人 用j个药 可以获得的最大经验值

然后就可以得出递推式

    for(int i=1;i<=n;++i)for(int j=1;j<=x;++j)if(j>=use[i])dp[i][j]=max(dp[i-1][j]+lose[i],dp[i-1][j-use[i]]+win[i]);elsedp[i][j]=dp[i-1][j]+lose[i];如果能打过,那么我可以选择打或者不打
如果打不过,那只能不打

但是这道题有一个注意点是,J可以从0开始,因为存在不用药就可以打过的情况,所以先初始化不用药的情况。

初始化

    for(int i=1;i<=n;++i)if(use[i]==0)dp[i][0]=dp[i-1][0]+win[i];elsedp[i][0]=dp[i-1][0]+lose[i];

AC代码

#include <bits/stdc++.h>
using namespace std;
const int MAXN=1e3+1;long long n,x,win[MAXN],lose[MAXN],use[MAXN],dp[MAXN][MAXN];
//dp[i][j]前i个人,使用j个药,能获得的最大经验值
int main(){ios::sync_with_stdio(false);cin.tie(nullptr),cout.tie(nullptr);cin>>n>>x;for(int i=1;i<=n;++i){cin>>lose[i]>>win[i]>>use[i];}for(int i=1;i<=n;++i)if(use[i]==0)dp[i][0]=dp[i-1][0]+win[i];elsedp[i][0]=dp[i-1][0]+lose[i];for(int i=1;i<=n;++i)for(int j=1;j<=x;++j)if(j>=use[i])dp[i][j]=max(dp[i-1][j]+lose[i],dp[i-1][j-use[i]]+win[i]);elsedp[i][j]=dp[i-1][j]+lose[i];cout<<dp[n][x]*5;return 0;
}
很明显这道题i和i-1也是滚动使用的
不过n的范围不是很大,所以不考虑优化

例题:疯狂的采药 - 洛谷

和上上题采药的区别就是,每个药可以无限采

每条线的含义都是一样的,也就是每个药都只能采一次。

7f69dc1f7a434ace919173e82497844c.png

这道题每个药都可以无限采,也就是说同一行之间也要去迭代所有情况

78ba15c6071344c0b40afea0ec02af41.png

上上题里面发现了,一维dp一定要逆序时间才能得出不迭代自己的状态,那么这道题正序迭代即可。

AC代码

#include <bits/stdc++.h>
using namespace std;
const int MAXM=1e4+1;
const int MAXT=1e7+1;int T,m,t[MAXM],v[MAXM];
long long dp[MAXT];int main(){ios::sync_with_stdio(false);cin.tie(nullptr),cout.tie(nullptr);cin>>T>>m;for(int i=1;i<=m;++i){cin>>t[i]>>v[i];}for(int i=1;i<=m;++i){for(int j=t[i];j<=T;++j){dp[j]= max(dp[j],dp[j-t[i]]+v[i]);}}cout<<dp[T];return 0;
}

j一定要从t[i]开始,不然下标要越界了。

如果要写出二维dp首先要改变定义,因为一个是只能用一次,一个是无限用,递推式必然不一样

定义dp[i][j]为采前i朵(无限采),消耗时间j内的最大价值不难得出以下两种情况
1.当前时间可以采走这支草药(1)继承先前状态(2)迭代自己综上dp[i][j]=max(dp[i-1][j],dp[i][j-t[i]]+v[i]);注意此处是i,不是i-12.当前时间不够采走这支草药,采不了那就不采,继承先前状态.  即dp[i][j]=d[i-1][j]

20230504 Update.

在隐隐约约或者分析出当前做的题目是dp题目的时候,dp题目的做法可以采取以下两种方式。

第一种:

1. 分析题目,提取关键。

2. 用数学语言表达问题。

3. 定状态转移方程。

4. 初始化。

5. 循环求解。

第二种:

1. 直接分析答案可能和哪些属性有关联。

2. 定状态转移方程。

3. 初始化。

4. 循环求解。

如果能用第一种分析出来那肯定是最好的,有了数学公式干什么都方便。

看例题。

[NOIP2012 普及组] 摆花 - 洛谷

因为本蒟蒻是蒟蒻(叉腰),所以直接用第二种方法。

可以发现摆花方案只和下面几种属性有关:
1. 花的种类

2. 花的数量,以及总数要小于一个限定数 -> 花的总数

3. 花的顺序

尝试做出定义 f[i][j] 为用上前 i 种花,且到当前为止已经用了 j 盆花的所有方案数。

当我们正序迭代这个式子的时候,可以发现 3. 花的顺序 这个属性被隐含解决了。

即这个定义目前看来还是可行的。

根据定义易得:f[i][j]=\sum_{k=j-a[i]}^j{f[i-1][k]},其中 1<=j<=m (不管他到现在最多能用多少,直接暴力枚举)。

初始化则为一种花都不用,一盆花都没有,即 f[i][0]=1 。其中 0<=i<=n 。

出现了 k=j-a[i] 所以循环的时候要注意下标越界,判断 k>=0 。

AC代码

#include <bits/stdc++.h>
using namespace std;
const int MOD=1e6+7,N=101;int n,m,f[N][N],a[N];int main(){cin>>n>>m;for(int i=1;i<=n;++i)cin>>a[i];for(int i=0;i<=n;++i)f[i][0]=1;for(int i=1;i<=n;++i)for(int j=1;j<=m;++j)for(int k=j-a[i];k<=j;++k)if(k>=0)f[i][j]=(f[i][j]+f[i-1][k])%MOD;cout<<f[n][m];return 0;
}

例题:[NOIP2008 普及组] 传球游戏 - 洛谷


http://chatgpt.dhexx.cn/article/VP9c9Lna.shtml

相关文章

ubuntu安装qt

Ubuntu安装qt 到qt官网下载“qt-opensource-linux-x64-5.9.1.run” 分别安装g, build-essential, libglq-mesa-dev, libglu1-mesa-dev freeglut3-dev 输入指令&#xff1a;”sudo apt-get install g” -> “sudo apt-get install build-essential” -> “sudo apt-get i…

QT安装、构建套件(MSVC、MinGW)配置

QT安装、构建套件(MSVC、MinGW)配置 1. QT框架及QT Creator下载 登录QT官网-https://www.qt.io/download。 点击downloads for open source users 在页面最下方&#xff0c;点击Download the QT online Installer。 在安装网页的最下方处有一行小字 “We do recommend yo…

QT linux安装

转载地址&#xff1a;http://www.cnblogs.com/tangkaixuan/p/6504102.html 文章来自https://lug.ustc.edu.cn/sites/qtguide/ 1.4 Qt在Linux下安装 Qt在Linux系统里的安装要稍微复杂一些&#xff0c;因为Linux发行版众多&#xff0c;所以安装过程有些差异。 由于Linux系统都可…

Qt国内镜像安装

下载安装程序 https://download.qt.io/official_releases/online_installers/ 使用国内镜像打开安装程序 G:\下载\qt-unified-windows-x64-4.5.1-online.exe --mirror https://mirror.nju.edu.cn/qt清华源 https://mirrors.tuna.tsinghua.edu.cn/qt/online/qtsdkrepository/win…

Qt下载与安装

一、Qt和Qt Creator的区别 Qt是C的一个库&#xff0c;或者说是开发框架&#xff0c;里面集成了一些库函数&#xff0c;提高开发效率。 Qt Creator是一个IDE&#xff0c;就是一个平台&#xff0c;一个开发环境&#xff0c;类似的比如说VS&#xff0c;也可以进行Qt开发&#xff…

Ubuntu 安装QT

一、最近这家公司接到一个订单&#xff0c;客户使用到国产操作系统&#xff0c;意味着需要使用到 Linux 系统&#xff0c;于是乎&#xff0c;之前的东西又要捡起来&#xff0c;而且&#xff0c;平时代码主要是windows 平台&#xff0c;这次需要将代码移植到linux 平台&#xff…

QT的安装------QT

由于现在的qt都是线上下载了&#xff0c;那我们就去qt的官网下载 download.qt.io 我们选择倒数第二个------archive(档案)&#xff0c;顺便学一下英文。 然后选择online install&#xff08;在线安装&#xff09; 身为先进人&#xff0c;当然选择最新的版本&#xff08;团新版…

VS+QT安装配置

心血来潮&#xff0c;装个QT&#xff0c;遇到好多问题&#xff0c;记录一下&#xff08;铭记那些踩过的坑&#xff09; 软件版本&#xff1a;vs是2022&#xff0c;qt是6.3.1 qt下载地址&#xff1a;Download Qt | Develop Desktop & Embedded Systems | Qt 点进去往下翻&…

Windows Qt安装教程

目录 一、Qt安装包下载二、Qt安装流程 一、Qt安装包下载 Qt官网&#xff1a;https://download.qt.io/ Qt官方提供的专门的资源下载网站&#xff0c;所有的开发环境和相关工具都可以从这里下载&#xff0c;Qt 官方下载网站如下图&#xff1a; 点击进入archive目录&#xff0c;…

Qt安装教程-详细

本文主要介绍了Qt5.9.7的安装步骤。 Qt下载 Qt的下载地址: http://download.qt.io/archive/qt/ qt-opensource-windows-x86-5.9.7.exe 是一个综合的安装包(5.8之前分开下载各个编译器版本SDK)&#xff0c;下载后安装的时候可以选择安装哪个编译器对应的SDK。一般可选MinGW 或…

QT下载安装

1、下载路径 https://download.qt.io/archive/qt/5.9/5.9.9/ 文件比较大&#xff0c;建议可以Qt 镜像网站 下载 国内著名的 Qt 镜像网站&#xff0c;主要是各个高校的&#xff1a;中国科学技术大学&#xff1a;http://mirrors.ustc.edu.cn/qtproject/ 清华大学&#xff1a;htt…

QT安装 and VS2019中安装QT插件

目录 VS中安装QT扩展插件1. 安装qt软件2. 在VS中安装qt扩展插件 VS中创建QT项目 VS中安装QT扩展插件 1. 安装qt软件 QT下载网址&#xff08;各版本&#xff09;. QT(5.12.11)直达界面. 根据操作系统不同&#xff0c;选择相应版本下载 双击运行… 输入QT账号信息&#xff0…

QT安装简介

1、下载QT安装包 下载网址&#xff1a;http://download.qt.io/ 或者http://download.qt.io/archive/qt/ 选择一个你需要的版本&#xff0c;例如 5.10 点击进去后&#xff0c;选择对应操作系统的安装包下载&#xff0c;例如qt-opensource-windows-x86-5.10.0.exe 2、安装QT …

QT安装教程(简易)

官方网址 Qt官方网址&#xff1a;http://download.qt.io 注意&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;这里下载记得要下载5版本以上的&#xff0c;因为5版本以上的自带打包工具&#xf…

Qt的安装及配置

一、Qt的安装 1.下载链接 或者 网盘下载 链接: https://pan.baidu.com/s/15Fwh8kOtrj4GIIg6ptnb7A 提取码: uvar 2.先注册账号&#xff0c;用自己的qq邮箱就可(注意&#xff1a;密码要有数字和大小写字母) 3.看图 4. 第一个&#xff1a;通过在Q中启用发送假名使用统计数据来…

Linux下的QT安装及初步使用过程(一)

目录 1.QT的安装 2.创建第一个QT程序 &#xff08;1&#xff09;QT代码&#xff08;C&#xff09; &#xff08;2&#xff09;使用qmake工具生成工程文件 ①确保qmake是可用的 ②如果不能找到qmake&#xff0c;则以下方式参考 ③使用qmake生成工程文件 ④生成Makefile文…

QT的安装 [新版2022]

QT的安装 [新版2022] 1 概述2 qt官网3 下载安装3.1 登录账号3.2 阅读同意许可3.3 开始安装3.4 帮助改进建议3.5 指定安装目录3.6 选择组件3.7 选择版本的依赖文件3.8 手动下载安装程序 1 概述 最近QT发布了6版本&#xff0c;5.x版本依然坚挺&#xff0c;官方也给出了LTS的标识…

【QT】简单易学的QT安装教程

对于在Windows系统上安装QT&#xff0c;经常会出现各种各样的错误&#xff0c;从不知选择版本到安装好后也无法使用&#xff0c;实属让人头疼。经过多次试错&#xff0c;找到了简单易学的QT方式&#xff0c;同时也会说明其中需要注意的点 一、QT安装和新建QT文件 https://dow…

Qt安装详细步骤

qt安装地址 安装步骤如下&#xff1a; &#xff08;上图为所勾选的编译组件详细勾选项&#xff0c;只适用于初学初学者&#xff09;

Qt安装教程(Qt 6.4)

Qt安装教程&#xff08;Qt 6.4&#xff09; 一、Qt简单介绍二、安装Qt&#xff08;1&#xff09;下载&#xff08;2&#xff09;安装 三、Qt组件一览&#xff08;1&#xff09; “Preview”分类下的开发组件&#xff08;2&#xff09; “Qt ”分类下的开发组件 一、Qt简单介绍 …