动态规划 路径类 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

Spring 核心技术解析【纯干货版】- XIV:Spring 消息模块 Spring-Jms 模块精讲

Spring 核心技术解析【纯干货版】- XIV:Spring 消息模块 Spring-Jms 模块精讲

在现代分布式系统中,消息队列(Message Queue,MQ)扮演着至关重要的角色,它不仅能够解耦系统各个模块,还能提升系统的可扩展性和可靠性。JMS(Java Message Service)作为 Java EE 规范中的一部分,为 Java 应用提供了一套标准的消息通信 API。然而,JMS 原生 API 相对复杂,涉及较多底层操作,而 Spring-JMS 模块的出现极大地简化了 JMS 在 Spring 应用中的使用,使得消息的发送与接收更加直观且易于维护。 本篇文章将深入解析 Spring-JMS 模块,介绍其核心功能,并通过 ActiveMQ 作为消息代理,提供一个 基于 XML 配置的完整示例,帮助开发者快速掌握 Spring-JMS 的使用方式。 文章目录 * 1、

By Ne0inhk
IoTDB AINode:SQL驱动时序AI全流程落地

IoTDB AINode:SQL驱动时序AI全流程落地

Apache IoTDB 作为开源时序数据库标杆,专为物联网场景设计,而 AINode 作为其原生AI节点,实现了“数据库即分析平台”的突破。AINode 可直接集成机器学习模型,通过标准SQL完成模型注册、管理与推理全流程,无需数据迁移或额外编程,支持毫秒级时序数据预测、异常检测等场景。本指南结合实操代码,从环境部署到工业级案例,手把手教你落地 AINode 应用。 一、核心概念与架构认知 1.1 核心组件分工 AINode 并非独立运行,需与 IoTDB 核心节点协同工作,三者职责明确: * ConfigNode:管理集群元数据与模型注册信息,协调各节点通信 * DataNode:存储时序原始数据,执行SQL解析与数据预处理 * AINode:加载模型文件,执行推理计算,返回分析结果 核心优势:通过“数据不动模型动”的架构,避免跨系统数据迁移,大幅降低时序AI落地成本。 1.

By Ne0inhk
Flutter for OpenHarmony: Flutter 三方库 mongo_dart 助力鸿蒙应用直连 NoSQL 数据库构建高效的数据流转系统(纯 Dart 驱动方案)

Flutter for OpenHarmony: Flutter 三方库 mongo_dart 助力鸿蒙应用直连 NoSQL 数据库构建高效的数据流转系统(纯 Dart 驱动方案)

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net 前言 在进行 OpenHarmony 的工业巡检、内部管理系统或边缘计算(Edge Computing)应用开发时,有时我们需要鸿蒙前端应用直接与后端的 MongoDB 数据库进行交互,而不仅仅是通过传统的 Web API 转发。 mongo_dart 是一个极其强大的、全功能、纯 Dart 实现的 MongoDB 驱动程序。它不依赖任何原生底层驱动(如 C 驱动),通过 Dart 的 Socket 机制直接实现 BSON 协议封装。这意味着你在鸿蒙设备上无需配置复杂的 NDK 动态库,即可拥有连接、查询、甚至是实时聚合分析 MongoDB 数据的能力。 一、网络直连架构模型

By Ne0inhk
Spring Cloud+AI :实现分布式智能推荐系统

Spring Cloud+AI :实现分布式智能推荐系统

欢迎文末添加好友交流,共同进步! “ 俺はモンキー・D・ルフィ。海贼王になる男だ!” 引言 * 在当今数字化时代,推荐系统已成为电商平台、内容分发平台、社交网络等互联网产品的核心竞争力之一。从淘宝的"猜你喜欢"、抖音的精准内容推送,到 Netflix 的影视推荐,优秀的推荐系统不仅能显著提升用户留存率和转化率,更能为企业带来可观的商业价值。据统计,亚马逊约 35% 的销售额来自推荐系统,Netflix 则通过推荐算法为用户节省了每年约 10 亿美元的搜索成本。 * 然而,随着业务规模的增长和推荐算法的复杂化,传统的单体架构逐渐暴露出诸多瓶颈。首先,推荐系统涉及用户画像构建、实时行为收集、特征工程、模型推理等多个环节,单体应用难以应对日益复杂的业务逻辑;其次,推荐服务需要处理海量并发请求,单机部署无法满足弹性伸缩的需求;再者,AI 模型的迭代更新日益频繁,单体架构下模型部署往往需要重启整个应用,严重影响线上服务稳定性;最后,企业需要支持 A/B

By Ne0inhk