动态规划 路径类 DP 入门:3 道经典例题(最小路径和 + 迷雾森林 + 过河卒)全解析

动态规划 路径类 DP 入门:3 道经典例题(最小路径和 + 迷雾森林 + 过河卒)全解析

文章目录


路径类 dp 是线性 dp 的⼀种,它是在⼀个 n × m 的矩阵中设置⼀个⾏⾛规则,研究从起点⾛到终点的
⽅案数、最⼩路径和或者最⼤路径和等等的问题。
⼊⻔阶段的《数字三⻆形》其实就是路径类 dp。

矩阵的最小路径和

题目描述

在这里插入图片描述

题目解析
1、状态表示
dp[i][j]表示从[1 1]格子走到[i j]格子时,所有方案下的最小路径和。
2、状态转移方程
我们还是以最后一步来推导状态转移方程,走到最后一个格子dp[n][m]有两种情况,分别是从dp[n - 1][m]到dp[n][m]和从dp[n][m - 1]到dp[n][m],所以dp[n][m]的取值就是取dp[n - 1][m]和dp[n][m - 1]的较小值加a[n][m],所以状态转移方程如下:
dp[i][j] = min(dp[i - 1][j],dp[i][j - 1])+ a[i][j]
3、初始化
因为本题填格子时需要访问左边和上边格子,所以我们需要处理边界情况,在填第一行和第一列格子时会访问到第0行和第0列,但是第0行和第0列是无意义的,所以我们需要把第0行和第0列初始化为无穷大,当填表访问到第0行或第0列格子时由于状态转移方程是取两个格子较小值,所以永远不会取到第0行或第0列格子。
还需要将dp[1][1] 初试化为a[1][1] ,因为[1 1]的最小路径和就是它本身,并且注意在填表时跳过[1 1]格子,首先因为已经将[1 1]格子初始化过了,第二如果把状态转移方程用于[1 1]格子,会把[1 1]格子填为无穷大。

在这里插入图片描述

4、填表顺序
从上往下,从左往右
5、输出结果
dp[n][m]
代码

#include<iostream>#include<cstring>usingnamespace std;constint N =510;int n, m;int a[N][N], dp[N][N];intmain(){//处理输入 cin >> n >> m;for(int i =1; i <= n; i++){for(int j =1; j <= m; j++){ cin >> a[i][j];}}//初始化memset(dp,0x3f3f3f3f,sizeof(dp)); dp[1][1]= a[1][1];//依序填表for(int i =1; i <= n; i++){for(int j =1; j <= m; j++){if(i ==1&& j ==1)continue; dp[i][j]=min(dp[i -1][j], dp[i][j -1])+ a[i][j];}}//输出结果 cout << dp[n][m]<< endl;}

迷雾森林

题目描述

在这里插入图片描述

题目解析
1、状态表示
dp[i][j]表示从[m 1]格子走到[i j]格子时,一共有多少种方案。
2、状态转移方程
还是根据最后一步推导状态转移方程,因为题目规定只能向上或向右走,所以假设最后一个格子为[i j],那么上一个格子可能是[i j - 1]或者[i + 1 j],那么格子[i j]的方案数就是[i j - 1]格子方案数加上[i + 1 j]格子方案数。
但是需要注意,本题只能走空地,如果遇到树是不能走的,也就是只有空地可以用状态转移方程填表,森林格子还是保持默认初始值0。
所以状态转移方程为:
a[i][j] = 0 --> dp[i][j] = dp[i j - 1] + dp[i + 1][j]
a[i][j] = 1 --> dp[i][j] = 0

在这里插入图片描述


3、初始化

  • 因为本题填表时会访问左边格子和下边格子,所以需要将dp数组第0列和第0行格子初始化为0,因为dp数组在全局开辟,所以值默认为0,不需要手动用memset初始化。
  • 因为本题是求解方案数,后续格子方案数就是从第一个格子累加而来,所以将dp[m][1]初始化为1(注意:填表时不填[m][1]格子)
    4、填表顺序
    因为根据前面推导的状态转移方程填表时需要访问左边格子和下边格子,所以填表顺序是从下往上填每一行,填每一行时遵循从左往右。
    5、输出结果
    dp[1][n]

注意:本题规定答案需要对2333取模。
代码

#include<iostream>#include<stdio.h>usingnamespace std;constint N =3010;constint MOD =2333;int m, n;int a[N][N], dp[N][N];intmain(){//处理输入 cin >> m >> n;for(int i =1; i <= m; i++){for(int j =1; j <= n; j++){scanf("%d",&a[i][j]);}}//初始化 dp[m][1]=1;//按序填表for(int i = m; i >=1; i--){for(int j =1; j <= n; j++){//[m][1]格子以及初始化,无需再次填if(i == m && j ==1)continue;if(a[i][j]==0) dp[i][j]=(dp[i][j -1]+ dp[i +1][j])% MOD;}}//输出结果 cout << dp[1][n];return0;}

过河卒

题目描述

在这里插入图片描述

题目解析
由于本题强制规定从[0 0]格子开始填表,所以为了方便处理数组越界问题,我们将题目输入的B 点坐标和马的坐标统一加1,这样就相当于将整个棋盘往下和往右移了一格,就可以从[1 1]格子开始填表
1、状态表示
dp[i][j]表示从[1 1]格子走到[i j]格子时,一共有多少种方案。
2、状态转移方程
还是根据最后一步推导状态转移方程,题目规定卒行走的规则只能向下、或者向右,所以状态转移方程:
dp[i][j] = dp[i][j - 1] + dp[i - 1][j]
但是需要注意只有走到不是马的控制点时才能用状态转移方程填表,如果走到马的控制点不做任何操作,让它保持初始值0即可。
3、初始化
本题需要对a、dp数组分别初始化,dp数组将[1][1]置为1即可,因为方案数问题的答案需要从第一个格子开始累加得到。
a数组的处理思路是将马的9个控制点全部置为1,其余点保持0,在后面根据状态转移方程填表时需要判断a数组对应格子是否为0,如果为0可以填表,不为0则直接continue,不做任何操作。本题对a数组初始化我们借助方向向量实现。
4、填表顺序
从左往右,从上往下
5、输出结果
dp[n][m]

注意:
1、马所在的点也是马的控制点。
2、dp数组要用long long类型,因为方案数类型题目的数据是阶层级别的。

代码

#include<iostream>usingnamespace std;typedeflonglong LL;constint N =25;int n, m, c, d;int a[N][N]; LL dp[N][N];//方向向量int dx[8]={2,2,1,1,-1,-1,-2,-2};int dy[8]={1,-1,2,-2,2,-2,1,-1};intmain(){//处理输入 cin >> n >> m >> c >> d; n++, m++, c++, d++;//初始化 dp[1][1]=1;for(int i =0; i <8; i++){ a[c + dx[i]][d + dy[i]]=1;} a[c][d]=1;//按序填表for(int i =1; i <= n; i++){for(int j =1; j <= m; j++){if(i ==1&& j ==1)continue;if(a[i][j]==0) dp[i][j]= dp[i][j -1]+ dp[i -1][j];}}//输出结果 cout << dp[n][m]<< endl;return0;}

以上就是小编分享的全部内容了,如果觉得不错还请留下免费的赞和收藏
如果有建议欢迎通过评论区或私信留言,感谢您的大力支持。
一键三连好运连连哦~~

在这里插入图片描述

Read more

【Java 开发日记】设计一个支持万人同时抢购商品的秒杀系统?

【Java 开发日记】设计一个支持万人同时抢购商品的秒杀系统?

目录 一、系统架构设计 1. 分层架构 2. 具体组件 二、核心问题解决方案 1. 超卖问题 解决方案一:Redis原子操作 解决方案二:数据库乐观锁 解决方案三:预扣库存 2. 高并发请求处理 2.1 流量削峰 2.2 分层过滤 3. 系统性能优化 3.1 缓存策略 3.2 读多写少优化 4. 详细实现方案 4.1 秒杀流程 4.2 库存同步方案 三、高可用保障 1. 限流降级策略 2. 熔断降级 四、监控与告警 1.

By Ne0inhk

Java最新面试题库——精选100道(含精简答案),收藏这篇就够了

JavaEE面试题整理 * 一、Java基础篇 * 二、JVM篇 * 三、Tomcat篇 * 四、MyBatis篇 * 五、Spring篇 * 六、SpringMVC面试题整理 * 七、Redis篇 * 八、Mongodb篇 * 九、MQ篇 * 十、Shiro篇 * 十一、搜索引擎篇 * 十二、Nginx篇 * 十三、SpringBoot篇 * 十四、Dubbo篇 一、Java基础篇 1、JAVA中的几种基本数据类型是什么,各自占用多少字节? 浮点类型:float(4字节)、double(8个字) 整数类型:byte(1字节)、short(2字节)、int(4字节)、long(8字节) 字符类型:char(

By Ne0inhk
JavaScript模式——策略模式:告别if-else地狱,2个案例让你“策略”在握

JavaScript模式——策略模式:告别if-else地狱,2个案例让你“策略”在握

1. 策略模式是什么鬼? 简单说,策略模式就是帮你把一堆“算法”或者“规则”分别打包,让它们可以随时互换。就像你去旅行,可以选择飞机、火车、自驾,目的地一样,但方式随便换。这种模式的核心思想就是把“做什么”和“怎么做”分开。 比如,你要计算年终奖,绩效S和绩效A的算法不一样,但它们都是“计算奖金”这件事。策略模式就让你把这些算法一个个封装好,想用哪个就用哪个。 2. 策略模式长啥样? 策略模式通常有两部分: * 策略对象:负责具体干活,比如不同的奖金算法、不同的动画缓动公式。 * 环境对象:负责接收命令,然后把活派给某个策略。 听起来抽象?没关系,下面两个例子保你一看就懂。 3. 案例一:年终奖怎么算? 3.1 新手写法:if-else堆成山 刚入行的程序员可能会这么写: var

By Ne0inhk
Java 大视界 -- Java 大数据机器学习模型在金融衍生品市场波动特征挖掘与交易策略创新中的应用(363)

Java 大视界 -- Java 大数据机器学习模型在金融衍生品市场波动特征挖掘与交易策略创新中的应用(363)

Java 大视界 -- Java 大数据机器学习模型在金融衍生品市场波动特征挖掘与交易策略创新中的应用(363) * 引言: * 正文: * 一、Java 构建的金融数据处理架构 * 1.1 多源异构数据实时融合 * 1.2 新闻舆情与市场冲击建模 * 二、Java 驱动的波动特征挖掘与预测 * 2.1 多模型融合的波动率预测 * 2.2 极端行情预警与止损优化 * 三、基于波动特征的交易策略创新 * 3.1 跨市场套利策略 * 3.2 动态对冲策略 * 四、实战案例:从 “被动止损” 到 “主动盈利” * 4.1 股指期货量化基金:从 38% 回撤到 12% * 4.2 外汇期权交易:

By Ne0inhk