动态规划(DP)——入门篇(11.24更新)

dp学习——入门篇 文章目录dp是什么如何发现一个题符合dpdp题目 dp是什么 dp也就是动态规划算法,空间换时间的思想。通过利用储存的子问题信息高效求出当前问题的最优解。 如何发现一个题符合dp 能够通过一个子问题推到另一个最优子结构,利用计算出的信息得到最优解。遵循一个顺序,重复计算子问题,且无后效性。其中具有最优子结构也可能是适合用贪心的方法求解。 dp题目 数字三角形 题意:找到一条路的权值和最大 思路:因... 阅读详情

零、先修课程

首先,在开始理解DP的思想前,你需要

1. 完成HDU里面的递推求解专题练习(For Beginner)那7道题(这些题很简单,题解请在博客中搜索),这对你理解DP有很大的帮助。

2. 对递归搜索(比如深度优先搜索,DFS)有一定了解。


一、递归与记忆化搜索

我们从POJ 3176入手来学习这一思想。(题目很短,请快速读完)

从上往下看,最大和值无非是往左走和往右走这两条路的较大者。这样,我们可以写出下面这个重要的递归函数:(这里i表示行,j表示列)

int f(int i, int j)
{
	if (i == n - 1) return a[i][j];
	return a[i][j] + max(f(i + 1, j), f(i + 1, j + 1));
}

但是,当我们编完整个程序并通过了样例后,提交后却返回了一串鲜红的文字:


What happened?

我们仔细分析程序,发现当我们从(0,0)往下走时,路越走越多,和细胞分裂一样,最终的路大约有2^n条!

这实在是太吓人了,当我们回过神来,程序早已崩溃(爆栈)。

冷静点,孩子。让我们再仔细地看看所给的数据吧,嗯,一个三角形大小的数据,准确地说,是n(n+1)/2个数。

按常理来说,实际的路也只有约n^2条,怎么可能会产生指数级别的路径数呢?

没错,重复。

重复的计算造成了程序的最终崩溃,而减少重复的计算是程序优化的一个永恒的主题。

那么,重复在哪里,如何减少重复的计算?

再来看看题目所给的样例,我们把目光放在1这个点上:我们可以从7-3-1到达1,继续往下面算,也可以从7-8-1到达1,继续往下面算。因此,在上面的代码中,在1这个点我们算了两遍!同样的遭遇也发生在1下面的那些点中。大量的重复计算最终导致了指数条路径的产生。

所以,我们不妨在计算一个点之前,先看看它有没有已经被计算出来。

怎么看?嗯……也许我们需要一个辅助的数组来保存计算的结果,比如这样:

int f(int i, int j)
{
	if (dp[i][j] >= 0) return dp[i][j];
	if (i == n - 1) return dp[i][j] = a[i][j];
	return dp[i][j] = a[i][j] + max(f(i + 1, j), f(i + 1, j + 1));
}
由于a[i][j]可能为0,所以需要在main()中把dp初始化为-1:

memset(dp, -1, sizeof(dp));
最终,程序返回了我们想看到的结果:



此题的完整代码见这篇文章。

PS:关于打印路径的方法,见下面的第三部分。


二、递推与状态转移方程

在数据更大情况下,即使我们用了记忆化搜索,递归仍可能会爆栈。

为对付这种情况,需要逆向思考

注意到上面的return语句实际上可以写成这种形式:

dp[i][j] = a[i][j] + max(dp[i + 1][j], dp[i + 1][j + 1])

我们称其为状态转移方程。

如果在计算dp[i][j]时,dp[i + 1][j],和dp[i + 1][j + 1]都已经计算出来就好了,如何做到这一点呢?——从下往上计算。

for (i = n - 1; i >= 0; --i)
    for (j = 0; j <= i; ++j)
        if (i == n - 1) dp[i][j] = a[i][j];
        else dp[i][j] = a[i][j] + max(a[i + 1][j], a[i + 1][j + 1]);
最后dp[0][0]就是我们想要的结果。

但是注意到数组a本身就具有状态转移的性质,所以上面的代码可以继续化简为

for (i = n - 2; i >= 0; --i)
    for (j = 0; j <= i; ++j)
        a[i][j] += max(a[i + 1][j], a[i + 1][j + 1]);
最后a[0][0]就是我们想要的结果。


此题的完整代码见这篇文章。


三、打印路径

在进行状态转移时(上面代码中的比较大小部分),如果附带记录下转移的来源,最后通过递归即可完成路径的打印(见代码)。


递归的写法:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn = 355;

int a[maxn][maxn], dp[maxn][maxn], n;
pair<int, int> path[maxn][maxn];

int f(int i, int j)
{
	if (dp[i][j] >= 0) return dp[i][j];
	if (i == n - 1) return dp[i][j] = a[i][j];
	if (f(i + 1, j) > f(i + 1, j + 1))
	{
		dp[i][j] = a[i][j] + f(i + 1, j);
		path[i][j] = make_pair(i + 1, j);
	}
	else
	{
		dp[i][j] = a[i][j] + f(i + 1, j + 1);
		path[i][j] = make_pair(i + 1, j + 1);
	}
	return dp[i][j];
}

void output(int i, int j)
{
	if (i == n - 1)
	{
		printf("%d %d\n", i, j);
		return;
	}
	printf("%d %d\n", i, j);
	output(path[i][j].first, path[i][j].second);
}

int main()
{
	int i, j;
	scanf("%d", &n);
	for (i = 0; i < n; ++i)
		for (j = 0; j <= i; ++j)
			scanf("%d", &a[i][j]);
	memset(dp, -1, sizeof(dp));
	printf("%d\n", f(0, 0));
	output(0, 0);
	return 0;
}

递推的写法:

#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn = 355;

int a[maxn][maxn], n;
pair<int, int> path[maxn][maxn];

void output(int i, int j)
{
	if (i == n - 1)
	{
		printf("%d %d\n", i, j);
		return;
	}
	printf("%d %d\n", i, j);
	output(path[i][j].first, path[i][j].second);
}

int main()
{
	int i, j;
	scanf("%d", &n);
	for (i = 0; i < n; ++i)
		for (j = 0; j <= i; ++j)
			scanf("%d", &a[i][j]);
	for (i = n - 2; i >= 0; --i)
		for (j = 0; j <= i; ++j)
		{
			if (a[i + 1][j] > a[i + 1][j + 1])
			{
				a[i][j] += a[i + 1][j];
				path[i][j] = make_pair(i + 1, j);
			}
			else
			{
				a[i][j] += a[i + 1][j + 1];
				path[i][j] = make_pair(i + 1, j + 1);
			}
		}
	printf("%d\n", a[0][0]);
	output(0, 0);
	return 0;
}

四、更多类型的DP(请在博客中搜索)

LCS
LIS
DAG上的最长路
整数拆分
区间DP


五、后续内容

背包、树形DP、概率DP、状态压缩

由于0-1背包和完全背包经常与其他DP相组合,具有较强的综合性,我把相关内容放在后续的中级篇进行介绍,敬请期待……


****转载请注明:http://blog.youkuaiyun.com/synapse7/article/details/16922779****

疯狂 dp(一) 线性 dp 动态规划是为了求解一种包含大量重叠子问题的最优化问题的方法。基本思想是,将原问题分解为若干相似的子问题,在求解的过程中通过子问题的解求出原问题的解。听起来和分治法很相似,但是,分治法只是不断地将问题分解成小问题求解,而动规之所以优秀是它会进行类似于记忆化搜索的过程,在求解的过程中把每一个子问题的解保存下来(不管后面会不会用到),然后在求解更大的问题时,从这些子问题的解里面寻求当前问题的最优解。所以 阅读详情

相关推荐

【牛客-21302】被3整除的子序列(动态规划)

【牛客-21302】被3整除的子序列(动态规划) 被3整除的子序列 题目描述 给你一个长度为50的数字串,问你有多少个子序列构成的数字可以被3整除答案对1e9+7取模 输入描述: 输入一个字符串,由数字构成,长度小于等于50 输出描述: 输出一个整数 示例1 输入 132 ...

Sky丨Star的博客 1099

算法精品讲解(2)——DP问题入门(适合零基础者,一看就会)

目录前言DP问题它是什么(了解)从中学的例题谈起再来说一下,DP问题的核心思想(理解)DP问题的解决方法先说方法论:再说具体的例子例一:例二:例三:DP和搜索的关系结语DP入门介绍:先画个饼吧哈哈。看完之后,你也可以。如下,是我们本节将要讲述的内容。我们将结合着LeetCode、洛谷和CodeForce上面的一些习题来作为我们的参考。难度循序渐进,直到大于等于LeetCode Hard。文章首发于wx公众h【自学编程村】 我们下面就进入正文。大名鼎鼎的DP问题,实际上就是Dynamic programmin

jxwd的博客 2万+

MATLAB实现基于动态规划(DP)进行无人机三维路径规划的详细项目实例

本项目基于动态规划(DP)算法实现无人机三维路径规划系统,主要创新点和成果如下: 核心技术实现 开发了多约束路径规划算法,综合考虑无人机速度、加速度等物理约束 采用增量式路径更新机制,实时响应动态障碍物变化 实现智能避障技术,通过传感器数据动态调整路径 系统架构 模块化设计包含环境建模、路径规划、避障等核心模块 支持GPU加速计算,提高大规模路径规划效率 提供API接口便于系统集成 应用价值 适用于无人机自主飞行、智能物流配送等场景 在紧急救援、环境监测等领域具有实际应用价值 创新点 融合动态规划与增量更新

优质创作者: 人工智能技术领域 对于有一定基础的 MATLAB和Python带具体需求都可以详谈 MATLAB和Python可以提供代码调试服务加油 谢谢 你的鼓励是我前行的动力 谢谢支持 加油 谢谢 2227

DP(动态规划)入门基础详解

DP总结 ps:学了近两个月的DP,是时候总结一波了… DP基本概要: 动态规划算法把原问题视作若干个重叠子问题的逐层递进,每一个子问题的求解过程都构成一个“阶段”。在完成前一个阶段的计算后,动态规划才会执行下一阶段的计算。 无后效性 : 为了保证这些计算能够按顺序,不重复进行,动态规划要求当前阶段不会对前面的阶段产生影响的基本条件。 最优子结构性质:大多数情况下,动态规划是用来求解最优化问题。此...

qq_44341728的博客 3337

GVF-B-snake算法 贪婪算法 DP算法

snakes:Active contour model Kass snakes,shapes and gradient vector flow Xu chenyang Using Dynamic Programming for Minimizing the Energy of Active Contours in the Presence of Hard Constraints Amir A. Amin A fast algorithm for active contours and curvature estimation Williams B-Spline Snakes: A Flexible Tool for Parametric Contour Detection Patrick Brigger,

DP(算法设计与分析)

对于由从1到N (1 <= N <= 39)这N个连续的整数组成的集合来说,我们有时可以将集合分成两个部分和相同的子集合。 例如,N=3时,可以将集合{1, 2, 3} 分为{1,2}和{3}。此时称有一种方式(即与顺序无关)。 N=7时,共有四种方式可以将集合{1, 2, 3, ..., 7} 分为两个部分和相同的子集合: {1,6,7} 和 {2,3,4,5} {2,5,7} 和 {1,3,4,6} {3,4,7} 和 {1,2,5,6} {1,2,4,7} 和 {3,5,6} 输入:程序从标准输入读入数据,只有一组测试用例。如上所述的N。 输出:方式数。若不存在这样的拆分,则输出0。 测试输入关于“测试输入”的帮助 期待的输出关于“期待的输出”的帮助 时间限制关于“时间限制”的帮助 内存限制关于“内存限制”的帮助 额外进程关于“{$a} 个额外进程”的帮助 测试用例 1 以文本方式显示 7↵ 以文本方式显示 4↵ 1秒 64M 0

DP入门

动态规划算法 他针对满足特定条件的一类问题,对各状态维度进行分阶段、有顺序、无重复、决策性的遍历求解。 dp入门,主要讲解dp里的几个经典的案例,通过案例来感受动态规划的主要思想 例题一:数字三角形 Description 7 3 8 8 1 0 2 7 4 4 4 5 2 6 5 (图一) 图一表示一个5行的数字三角形。假设给定一个n行数字三角形,计算出从三角形顶至底的一条路径,使该路径经过的数字总和最大。每一步只能由当前位置向左下或右下。 Input 你的程序要能接受标准输入。第一行包含一个整数T,表示

qq_53737964的博客 1405

DP算法即动态规划算法集锦

DP算法即动态规划算法。里面有几个我从网上搜集的经典案例

【算法】动态规划专题⑪ —— 区间DP python

区间动态规划(区间DP)适用于解决涉及区间最优化的经典问题,如石子合并、最长回文子序列等。

查理零世的博客 1897

Windows 10/11 零基础配置 SillyTavern 连接 Claude API 完整指南(含 Node.js 环境搭建)

本文提供Windows 10/11系统下零基础配置SillyTavern连接Claude API的完整教程,涵盖Node.js环境搭建、SillyTavern初始化及API连接配置等关键步骤。通过详细的操作指南和实用技巧,帮助用户快速搭建AI聊天界面并优化对话体验,实现与Claude AI的高效交互。

weixin_30642561的博客 134

Android逆向实战:通过smali/baksmali动态注入DEX逻辑

本文详细介绍了在无源码条件下对Android应用进行动态逻辑注入的实战方法。通过使用smali和baksmali工具对DEX文件进行反编译、修改与重打包,实现在目标方法中插入自定义代码(如日志打印),并分享了定位目标、处理寄存器、避免常见错误的实用技巧与避坑指南。

weixin_29008691的博客 364

C++动态规划模板汇总大全

1.背包模型2.状态机模型3.状态压缩DP4.区间DP5.树形DP6.数位DP7.单调队列优化DP8.斜率优化DP(因为我还没学,所以暂时不更新,请见谅)

m0_54615144的博客 2185

rk3588 适配音频解码芯片 ALC5616

在 RK3588 的高性能方案中,选择 **ALC5616**(来自 Realtek 瑞昱)通常意味着项目更侧重于**音频性能的稳定性**以及**全球供应链的通用性**。

weixin_35723192的博客 145

【DP 动态规划 | 精选推荐】持续更新

本文(DP 动态规划)章节,主要记录一些比较经典,或者说是容易理解的动态规划习题,难度一般都不会很大(≤Medium≤Medium)。

Cauchy的博客 992

【QNX+Android虚拟化方案】01 - 芯片介绍

【QNX+Android虚拟化方案】01 - 芯片介绍

|~~~热爱生活、努力学习的小伙汁~~~| 2948

【实战篇导读】四足机器人运动控制导读

认知有限,望大家多多包涵,有什么问题也希望能够与大家多交流,共同成长!本文先对四足机器人运动运动控制系统及相关算法做个简单的介绍,具体内容后续再更,其他模块可以参考去我其他文章主要介绍了波士顿spot机器人的发展历史、四足机器人的硬件系统及电机执行器组成包括硬件系统、关节电机执行器,机载传感器机处理器及电气连接等。概述了四足机器人规划控制、感知、定位及局部地图的相关算法方向。

qq_35635374的博客 679

动态规划.

‍......

snowfoootball的博客 2218

【OpenClaw从入门到精通】命令行操作指南——TUI模式高效管理OpenClaw(2026避坑版)

本文将详细介绍OpenClaw的命令行操作,重点介绍TUI(Text User Interface)模式的高效使用方法,以及在实际使用中需要避免的常见陷阱。通过本文的学习,你将掌握OpenClaw命令行的精髓,提升工作效率。

一键难忘的博客 1692

动态规划——特殊线性dp:子序列和子串问题

最长单调(不上升或不下降)子序列,最长公共子序列(子串)

m0_73693552的博客 1074

基于STM32L4XX、HAL库的CH182H1以太网收发器(PHY)驱动程序设计

define PHY_REG_PHYSCR 0x12 // PHY特殊控制寄存器。#define PHY_REG_PHYSTS 0x10 // PHY状态寄存器。#define PHY_REG_PHYIDR 0x11 // PHY标识寄存器。#define PHY_REG_ID1 0x02 // PHY标识符1。#define PHY_REG_ID2 0x03 // PHY标识符2。

zhusl6688的博客 406

华为OD机试真题 新系统 2026-09-06 Python&JS【仓储货物有序出库】

某仓储中心有一个货物堆放区,货物编号按乱序堆叠存放,只能从顶部取货,管理员需要将货物按编号从小到大取出。

MISAYAONE的博客 116

Abandon の DP-动态规划【专辑】(长期更新)

—— “Hala, Abandon!”~ ------------------------------------------------------------- ◊线性DP:   经典DP原型系列:   °NOIP 1999 拦截导弹 (最长下降子序列入门)   思路:比较简单,第一问最长不上升子序列;第二问贪心即可(有人证明第二问等价于求最长上升子序列。。。)。   ...

weixin_30323961的博客 97

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

四边形优化DP 和 插头dp引入

四边形不等式是一种用于优化动态规划(DP)问题的数学工具,主要适用于形如 fi​=minj<i​{fj​+cost(j,i)} 的转移方程。其核心思想是通过证明代价函数满足特定的不等式性质,从而缩小状态转移时的决策变量范围,将时间复杂度从 O(n2) 优化至 O(nlogn) 或更低。插头 DP 是一种用于解决棋盘路径、回路等状态压缩动态规划问题的方法。

2401_84910613的博客 637

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

24.11.24(24ICPC沈阳站游记)

思考后没发现能用什么数据结构,然后看了下样例,发现胜负改变时即区间长度len和d的奇偶性不同,很快写了发后交上去wa了,然后陆陆续续改了点,又wa了两发,封榜时发现了5 2,5 4这样的样例,写了个程序证明只要是长度为5,d为2,4,操作奇偶性一定不会变,于是想到了gcd与len的奇偶不同才会改变胜负,很快改了后就过了,此时已封榜18min。这样 i的路径数即为存在2,3,4的质因子的aj的路径数 dp【j】的总和,再把dp【i】的值加到质因子2,3,4上,但这样会出现重复计算的问题。

breath_of_yuan的博客 981

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

上一篇: POJ 3176 Cow Bowling (简单DP)
下一篇: 用位运算生成分形
synapse7
博客等级 码龄14年 432粉丝 701原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值