数据结构与算法学习笔记(Acwing提高课)----动态规划·数字三角形

数据结构与算法学习笔记----动态规划·数字三角形

@@ author: 明月清了个风
@@ first publish time: 2025.4.23

ps⭐️终于开始提高课的题啦,借的人家的号看,以后给y总补票叭,提高课的题比之前的多很多啊哈哈哈哈,基本上每种题型都对应了难度逐步上升的几道题,和基础课的题相比加了一层应用,需要从题目中抽象出模型才能解题。

数字三角形的题为动态规划的经典模型,基础课中已经讲过了,在这里。提高课的是在此基础上的进一步延伸和应用,但原理其实没有变。

Acwing 1015. 摘花生

Hello Kitty想摘点花生送给她喜欢的米老鼠。

她来到一片有网格状道路的矩形花生地(如下图),从西北角进去,东南角出来。

地里每个道路的交叉点上都有种着一株花生苗,上面有若干颗花生,经过一株花生苗就能摘走该它上面所有的花生。

Hello Kitty只能向东或向南走,不能向西或向北走。

问Hello Kitty最多能够摘到多少颗花生。

1.gif

输入格式

第一行是一个整数 T T T,代表一共有多少组数据。

接下来是 T T T组数据。

每组数据的第一行是两个整数,分别代表花生苗的行数 R R R和列数 C C C

每组数据的接下来 R R R行数据,从北向南依次描述每行花生苗的情况。每行数据有 C C C个整数,按从西向东的顺序描述了该行每株花生苗上的花生数目 M M M

输出格式

对每组输入数据,输出一行,内容为Hello Kitty能摘到得最多的花生颗数。

数据范围

1 ≤ T ≤ 100 1 \le T \le 100 1T100

1 ≤ R , C ≤ 100 1 \le R,C \le 100 1R,C100

0 ≤ M ≤ 1000 0 \le M \le 1000 0M1000

思路

对于动态规划而言,和基础课一样,考虑状态表示和状态转移。

对于状态表示,使用f[i][j],表示的集合是所有从(1, 1)走到(i, j)的路线,而要求的属性是这个集合中的最大值。

对于状态转移或者说状态计算,就是将上述集合进行划分,找出其子集递归求解。而状态划分的很重要的一种方法就是根据最后一步操作进行划分,也就是f[i][j]是怎么来的这一步。在这一题中,f[i][j]可以是从上面下来,也可以是从左边过来,因此可以划分为两个子集f[i][j - 1]f[i - 1][j]。需要注意的是,对于集合的划分,最重要的两个原则是不重不漏,其中不漏是任何情况都需要满足的,因为每种方案都需要考虑,而不重在求最大值或最小值时可以不满足,因为重复的计算并不会提高集合中的最值。

在上面已经将集合f[i][j]划分为两个集合f[i - 1][j]f[i][j - 1],那么求f[i][j]的最大值就是求这两个子集的最大值再加上最后一步的权值即可。

代码

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>using namespace std;const int N = 110;int T;
int n, m;
int a[N][N];
int f[N][N];int main()
{cin >> T;while(T --){cin >> n >> m;for(int i = 1; i <= n; i ++)for(int j = 1; j <= m; j ++)cin >> a[i][j];for(int i = 1; i <= n; i ++)for(int j = 1; j <= m; j ++)f[i][j] = max(f[i - 1][j], f[i][j - 1]) + a[i][j];cout << f[n][m] << endl;}return 0;
}

Acwing 1018. 最低通行费

一个商人穿过一个 N × N N \times N N×N的正方形的网格,去参加一个非常重要的商务活动。他要从网格的左上角进,右下角出。每穿越中间1个小方格,都要花费1个单位时间。商人必须在 2 N − 1 2N - 1 2N1个单位时间穿越出去。而在经过中间的每个小方格时,都需要缴纳一定的费用。
这个商人期望在规定时间内用最少费用穿越出去。请问至少需要多少费用?
注意:不能对角穿越各个小方格(即,只能向上下左右四个方向移动且不能离开网格)。

输入格式

第一行是一个整数 T T T,表示正方形的宽度 N N N

后面 N N N行,每行 N N N个不大于 100 100 100的整数,为网格上每个小方格的费用。

输出格式

输出一个整数,表示至少需要的费用。

数据范围

1 ≤ N ≤ 100 1 \le N \le 100 1N100

思路

其实还是和数字三角形一样的模型,只是多了个迷惑的地方,给了步骤限制为 2 N − 1 2N - 1 2N1,但是可以发现只要不回头走就行。因此思路和代码都是和上面一样的。

需要注意的地方时,这一题求的集合的属性是最小值,因此需要对边界的地方进行特判和初始化。

代码

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>using namespace std;const int N = 110, inf = 1e9;int n;
int f[N][N];
int a[N][N];int main()
{cin >> n;for(int i = 1; i <= n; i ++)for(int j = 1; j <= n; j ++)cin >> a[i][j];for(int i = 1; i <= n; i ++)for(int j = 1; j <= n; j ++){if(i == 1 && j == 1) f[i][j] = a[i][j];else{f[i][j] = inf;if(i > 1) f[i][j] = min(f[i][j], f[i - 1][j] + a[i][j]);if(j > 1) f[i][j] = min(f[i][j], f[i][j - 1] + a[i][j]);}}cout << f[n][n] << endl;return 0;
}

Acwing 1027. 方格取数

设有N*N的方格图( N ≤ 10 N \le 10 N10),我们将其中的某些方格中填入正整数,而其他的方格中则放入数字0。
某人从图的左上角的 A A A ( 1 , 1 ) (1, 1) (1,1)出发,可以向下行走,也可以向右走,直到到达右下角的B点 ( N , N ) (N, N) (N,N)。在走过的路上,他可以取走方格中的数(取走后的方格中将变为数字0)。
此人从 A A A点到 B B B点共走两次,试找出2条这样的路径,使得取得的数之和为最大。

输入格式

第一行是一个整数 N N N,表示 N × N N \times N N×N的方格图。

接下来每行有三个整数,第一个为行号数,第二个为列号数,第三个为在该行、该列上所放的数。

一行以"0 ,0 ,0"表示结束。

输出格式

给出一个整数,表示两条路径上取得的最大的和。

数据范围

$ N \le 10$

思路

这一题的变化是要走两次。对于上面两题而言都只需要计算一个路径,而走两次最大的变化就是同一个数只能被拿走一次。

首先思考是两条路线一起走还是两条路线先后走,因为每个数只能拿一次,因此两条路线是完全独立的两条线,先走后走并不影响总和。可以直接对上面的状态表示进行推广,使用f[i1][j1][i2][j2]表示第一条路线走到了a[i1][j1],第二条路线走到了a[i2][j2]的集合。

然后就是如何不重复走到同一个格子,因为不能回头走,所以如果两条线走到了同一个格子,那么肯定有 i 1 + j 1 = i 2 + j 2 i_1 + j_1 = i_2 + j_2 i1+j1=i2+j2

因此就有了下面的优化,上面这个状态表示是四维的,虽然数据范围很小,但是仍有优化空间,考虑到要对每个格子的坐标的和作比较,因此使用f[k][i1][i2]进行表示,其中k表示横纵坐标的和,那么就有j1 = k - i1j2 = k - i2,当i1 == i2时,可能出现两条路线重合。

对于状态划分来说,和之前的题目一样,使用最后一步进行划分,只是这里会有两条路线。因此可以根据第一条路线向右或向下,第二条路线向右或向下分为四类:下下,下右,右下,右右。

以下下的组合为例说明状态计算方法:

  • 对于下下的组合来说,两条路线都是向下走到了最新的一步,因此是分别从a[i1][j1 - 1]a[i2][j2 - 1]走到了最新位置,这两个地方的状态可以由 f [ k − 1 ] [ i 1 ] [ i 2 ] f[k - 1][i1][i2] f[k1][i1][i2]表示,然后判断一下是否重合即可,重合的话那就只加上一个a[i1][j1]即可,不重合就要同时加上a[i1][j1]a[i2][j2]

代码

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>using namespace std;const int N = 20;int n;
int f[N * 2][N][N];
int w[N][N];int main()
{cin >> n;int a, b, c;while(cin >> a >> b >> c, a || b || c) w[a][b] = c;for(int k = 2; k <= n + n; k ++)for(int i1 = 1; i1 <= n; i1 ++)for(int i2 = 1; i2 <= n; i2 ++){int j1 = k - i1, j2 = k - i2;if(j1 >= 1 && j1 <= n && j2 >= 1 && j2 <= n){int t = w[i1][j1];if(i1 != i2) t += w[i2][j2];int &x = f[k][i1][i2];x = max(x, f[k - 1][i1- 1][i2 - 1] + t);x = max(x, f[k - 1][i1 - 1][i2] + t);x = max(x, f[k - 1][i1][i2 - 1] + t);x = max(x, f[k - 1][i1][i2] + t);}}cout << f[n + n][n][n] << endl;return 0;
}

Acwing 275. 传纸条

小渊和小轩是好朋友也是同班同学,他们在一起总有谈不完的话题。

一次素质拓展活动中,班上同学安排坐成一个 m m m n n n列的矩阵,而小渊和小轩被安排在矩阵对角线的两端,因此,他们就无法直接交谈了。

幸运的是,他们可以通过传纸条来进行交流。

纸条要经由许多同学传到对方手里,小渊坐在矩阵的左上角,坐标 ( 1 , 1 ) (1, 1) (1,1),小轩坐在矩阵的右下角,坐标 ( m , n ) (m, n) (m,n)

从小渊传到小轩的纸条只可以向下或者向右传递,从小轩传给小渊的纸条只可以向上或者向左传递。

在活动进行中,小渊希望给小轩传递一张纸条,同时希望小轩给他回复。

班里每个同学都可以帮他们传递,但只会帮他们一次,也就是说如果此人在小渊递给小轩纸条的时候帮忙,那么在小轩递给小渊的时候就不会再帮忙,反之亦然。

还有一件事情需要注意,全班每个同学愿意帮忙的好感度有高有低(注意:小渊和小轩的好心程度没有定义,输入时用 0 0 0表示),可以用一个 0 ∼ 100 0 \sim 100 0100的自然数来表示,数越大表示越好心。

小渊和小轩希望尽可能找好心程度高的同学来帮忙传纸条,即找到来回两条传递路径,使得这两条路径上同学的好心程度之和最大。

现在,请你帮助小渊和小轩找到这样的两条路径。

输入格式

第一行有 2 2 2个用空格隔开的整数 m m m n n n,表示学生矩阵有 m m m n n n列。

接下来 m m m行是一个 m × n m \times n m×n的矩阵,矩阵中第 i i i j j j列的整数表示坐在第 i i i j j j列的学生的好心程度,每行的 n n n个整数之间用空格隔开。

输出格式

输出一个整数,表示来回两条路上参与传递纸条的学生的好心程度之和的最大值。

数据范围

1 ≤ n , m ≤ 50 1 \le n, m \le 50 1n,m50

思路

这一题与上一题的区别是同一个格子不能走第二次,在上一题中,如果走到了重合的格子会少加一次权重,而这里要求是不能走第二次,其实也很好解决,也就是要证明有两条不相交的路线具有最大的好心程度之和。

首先要解决的是两条路线相交的问题,如果存在两条相交的路线,如下图所示,从A到B的两条路线中有两个交点C与D。

图1

因为经过的格子的权重是确定的,因此可以对两条路线上的部分片段进行交换,变换后入下图:

在这里插入图片描述

因此得证相交的路线可以通过路线的变形使其变成等效但仅有相交点而不相错的情况。

根据题意,同一个点不能走第二次,因此考虑如何处理相交点 C C C D D D。通过观察上图可以发现,对于重合点C来讲,该路线可以转化为下图所示经过点E的路线,且该路线的好心程度之和一定优于原路线(将重合点C权重清零后第二次不再计算)

在这里插入图片描述

根据上一题的代码可以发现,其实当走到同一点时,我们只会添加一次权重,因此这里的值一定会比经过点E的路线值小,也就是题目所限制的非法路线算出来的值一定小于最优解合法路的值,因此上一题的代码中不必为了这一题进行修改,代码稍有变动。

代码

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>using namespace std;const int N = 60;int n, m;
int f[N * 2][N][N];
int w[N][N];int main()
{cin >> n >> m;for(int i = 1; i <= n; i ++)for(int j = 1; j <= m; j ++)cin >> w[i][j];for(int k = 2; k <= n + m; k ++)for(int i1 = 1; i1 <= n; i1 ++)for(int i2 = 1; i2 <= n; i2 ++){int j1 = k - i1, j2 = k - i2;if(j1 >= 1 && j1 <= m && j2 >= 1 && j2 <= m){int t = w[i1][j1];if(i1 != i2) t += w[i2][j2];int &x = f[k][i1][i2];x = max(x, f[k - 1][i1- 1][i2 - 1] + t);x = max(x, f[k - 1][i1 - 1][i2] + t);x = max(x, f[k - 1][i1][i2 - 1] + t);x = max(x, f[k - 1][i1][i2] + t);}}cout << f[n + m][n][n] << endl;return 0;
}

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

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

相关文章

阿里巴巴安全工程师面试题:BAS

阿里巴巴新发布了针对应届生的安全工程师招聘岗位&#xff0c;岗位要求&#xff1a; 研究新型前沿攻防技术&#xff0c;验证正向和防御安全产品能力的有效性&#xff0c;挖掘其规则或引擎漏洞&#xff0c;并利用BAS&#xff08;Breach and Attack Simulation&#xff09;建立自…

【正则表达式】正则表达式使用总结

正则表达式除了匹配普通字符外,还可以匹配特殊字符,这些特殊字符被称为“元字符”。‌ 特殊字符(元字符) ‌限定符‌:用于指定正则表达式中某个组件的出现次数。常见的限定符包括: *:0次或多次 +:1次或多次 ?:0次或1次 {n}:恰好n次…

数据库对象与权限管理-Oracle数据字典详解

1. 数据字典概念讲解 Oracle数据字典是数据库的核心组件&#xff0c;它存储了关于数据库结构、用户信息、权限设置和系统性能等重要的元数据信息。这些信息对于数据库的日常管理和维护至关重要。数据字典在数据库创建时自动生成&#xff0c;并随着数据库的运行不断更新。 数据…

链表系列一>两数相加

目录 题目&#xff1a;解析&#xff1a;方法&#xff1a;代码&#xff1a;链表常用技巧&#xff1a; 题目&#xff1a; 链接: link 解析&#xff1a; 方法&#xff1a; 代码&#xff1a; /*** Definition for singly-linked list.* public class ListNode {* int val;* …

FreeRTOS深度解析:队列集(Queue Sets)的原理与应用

FreeRTOS深度解析&#xff1a;队列集&#xff08;Queue Sets&#xff09;的原理与应用 什么是队列集&#xff1f; 在FreeRTOS中&#xff0c;队列集&#xff08;Queue Sets&#xff0c;英文名xQueueSet&#xff09;是一种强大的数据结构&#xff0c;用于高效管理多个队列。它的…

QT creater和vs2017文件路径问题

1. \\双反斜杠&#xff0c;传统写法&#xff0c;需转义 在 C/C 字符串中&#xff0c;\ 具有特殊含义&#xff0c;例如&#xff1a; \n 表示换行 \t 表示制表符 \" 表示双引号 如果要表示一个真正的反斜杠&#xff0c;必须写成 \\&#xff0c;否则编译器会将其解释为转…

对流对象的理解

在c里&#xff0c;“流”可以理解为数据传输与操作的“介质”。 从输入输出角度来看&#xff0c;有输入流&#xff08;比如cin&#xff09;和输出流&#xff08;cout&#xff09;。对于输入流&#xff0c;数据通过它从外部设备&#xff08;例如键盘&#xff09;“流入”程序内…

Visium HD多样本拼片拆分

Visium HD实验的时候一个捕获区域内可以包含多个样本拼片&#xff08;例如多个组织切片或不同样本的排列&#xff09;是常见的实验设计&#xff0c;多样本拼片能够提升实验效率&#xff0c;单张玻片处理多个样本&#xff0c;降低试剂和测序成本&#xff0c;后续分析的时候只需要…

进程(Process)详解

进程&#xff08;Process&#xff09;详解 一、基本定义 ‌概念‌ 进程是计算机中程序的一次动态执行实例&#xff0c;包含程序代码、数据及运行状态&#xff0c;是操作系统进行资源分配和调度的基本单位‌。与静态的“程序”不同&#xff0c;进程是动态实体&#xff0c;随程…

毕业论文超清pdf带标签导出

Word直接导出的pdf不够清晰&#xff0c;使用打印导出的pdf又不带书签以及目录跳转功能这一问题&#xff0c;查阅网上资料使用Adobe DC似乎能够解决但是下载安装比较麻烦&#xff0c;于是写了python程序解决该问题。 解决思路&#xff1a; 使用python脚本对两个pdf文件进行合并…

NOIP2012提高组.同余方程

目录 题目算法标签: 数论, 扩展欧几里得算法思路代码 题目 203. 同余方程 算法标签: 数论, 扩展欧几里得算法 思路 简单的扩展欧几里得算法应用题, 扩展欧几里得算法可以直接计算同余方程的通解, 因为求得是最小正整数解, 因此需要取模转换为正整数 a x b y ≡ 1 ax by …

C++学习-入门到精通-【0】计算机和C++简介

C学习-入门到精通-[0]计算机和C简介 计算机和C简介 C学习-入门到精通-[0]计算机和C简介一、计算机的组成二、硬件和软件三、数据的层次结构四、机器语言、汇编语言和高级语言五、C标准库六、面向对象技术 一、计算机的组成 计算机是由多个不同功能的逻辑单元组成的&#xff1a…

macOS 系统设置息屏情况下,PHP等后台脚本继续执行

在 macOS 系统下&#xff0c;当屏幕息屏或合上盖子时&#xff0c;后台脚本程序是否会继续运行&#xff0c;主要取决于以下几个因素&#xff1a; 1. 系统睡眠状态的影响 默认情况&#xff1a;合盖/息屏后&#xff0c;Mac 会进入「睡眠模式」&#xff08;部分硬件休眠&#xff…

SpringBoot集成ActiveMQ异常处理机制:若未捕获异常,消息会被重新投递

一、问题描述 SpringBoot项目集成AvtiveMQ&#xff0c;作为消息消费者。如果在消费消息的方法中&#xff0c;抛出异常&#xff0c;会产生什么效果&#xff1f; 二、ActiveMQ异常处理机制&#xff08;AI问答仅供参考&#xff09; 在Spring Boot项目集成ActiveMQ作为消息消费者…

【Java学习笔记】random的使用

random使用方法 使用说明&#xff1a;返回的是(0<n<1)这个范围中的任意带正号的double值 代码实例 public class helloworld{public static void main(String[] args){System.out.println(Math.random());} }生成0-100中的任意数代码示例 public class Main {public …

(三)垂直分库架构、分布式数据库

文章目录 垂直分库架构/分布式数据库什么是垂直分库架构架构模型优缺点优点缺点 技术案例分布式数据库架构模型优缺点优点缺点 技术案例 垂直分库架构/分布式数据库 什么是垂直分库架构 根据业务的模块划分&#xff0c; 将不同业务的数据放到不同的数据库中。 比如一个电子商城…

数据结构线性表的顺序存储结构

线性表是由零个或多个数据元素组成的有序序列。 特点&#xff1a; 数据元素间是有顺序的&#xff1b; 数据元素的个数是有限的&#xff1b; 一般来说&#xff0c;数据元素的类型是相同的&#xff08;强类型语言&#xff09;。c/c是强类型语言&#xff0c;必须指定数据类型。…

扣子空间试用:生成五一骑行规划+notion文章编写

今天试用了一下扣子空间&#xff0c;正好五一快到了&#xff0c;让它帮忙做了五一骑行规划&#xff0c;效果不赖&#xff01; 生成五一骑行规划 点击前往网站查看效果 prompt 如下&#xff1a; 帮我做一个五一上海骑行规划 要求&#xff1a; - 风景优美 - 人少 - 100km总路程…

最新得物小程序sign签名加密,请求参数解密,响应数据解密逆向分析

点击精选&#xff0c;出现https://app.dewu.com/api/v1/h5/index/fire/index 这个请求 直接搜索sign的话不容易定位 直接搜newAdvForH5就一个&#xff0c;进去再搜sign&#xff0c;打上断点 可以看到t.params就是没有sign的请求参数&#xff0c; 经过Object(a.default)该函数…

在C#串口通信中,一发一收的场景,如何处理不同功能码的帧数据比较合理,代码结构好

在 C# 串口通信的一发一收场景里&#xff0c;处理不同功能码的帧数据可采用以下合理的代码结构&#xff0c;它能让代码更具可读性、可维护性和可扩展性。 实现思路 定义帧结构&#xff1a;创建一个类来表示通信帧&#xff0c;其中包含功能码、数据等信息。功能码处理逻辑&…