动态规划——数字三角形模型

母题:数字三角形

集合:f[i][j]表示所有从起点走到 ( i , j ) (i,j) (i,j)的路径
属性:f[i][j]存的数是集合中所有路径和的最大值

状态计算:对于每一条从起点到 ( i , j ) (i,j) (i,j) 的路径,其要么是从左上方来的,要么是从右上方来的。

左上方:f[i][j] = f[i-1][j-1] + a[i][j] (a[i][j]为终点的值)
右上方:f[i][j] = f[i-1][j] + a[i][j] ,两者再取max即可。

#include <bits/stdc++.h>using namespace std;const int N = 510, INF = 0x3f3f3f3f;
int f[N][N], a[N][N];int main()
{int n;cin >> n;for (int i = 1; i <= n; i++) for (int j = 1; j <= i; j++) cin >> a[i][j];for (int i = 1; i <= n; i++) for (int j = 0; j <= i + 1; j++) f[i][j] = -INF;f[1][1] = a[1][1];//顶点就是那个数for (int i = 2; i <= n; i++)//从第二行开始状态计算for (int j = 1; j <= i; j++)f[i][j] = max(f[i-1][j-1] + a[i][j], f[i-1][j] + a[i][j]);int ans = -INF;for (int i = 1; i <= n; i++) ans = max(ans, f[n][i]);//f[n][i]表示从起点走到最后一行的第i个数的路径最大值,这i个数之间彼此还要再比出一个最大值cout << ans;        return 0;
}

需要注意的是初始化,由于整数有可能是负数,且属性是 M a x \rm Max Max,所以仅全局的默认初始化为 0 0 0 是不行的,需要初始化为负无穷

此外,等腰三角形的左右两条边分别不能从左上右上递推,因此初始化时需要考虑边界, j j j 0 0 0 开始到 i + 1 i+1 i+1 结束,但 i i i 无需更改,因为只是左右的边界问题。

为了避免边界问题,本题也可以采取由下到上 d p dp dp 方式。
从下到上的话,所有的点(包括左右两条边上的点)都一定可以从左下和右下两个方向递推,因此不需要考虑边界,也无需初始化。状态转移方程为f[i][j] = max(f[i+1][j+1] + a[i][j], f[i+1][j] + a[i][j])

#include <bits/stdc++.h>using namespace std;const int N = 510, INF = 0x3f3f3f3f;
int f[N][N], a[N][N];int main()
{int n;cin >> n;for (int i = 1; i <= n; i++) for (int j = 1; j <= i; j++) cin >> a[i][j];for (int i = 1; i <= n; i++) f[n][i] = a[n][i];//初始化最后一行for (int i = n-1; i >= 1; i--)//从倒数第二行往上计算状态for (int j = 1; j <= i; j++)//与左右无关,不影响顺序f[i][j] = max(f[i+1][j+1] + a[i][j], f[i+1][j] + a[i][j]);cout << f[1][1];//最后枚举到顶点(1,1),f[1][1]即为最大值return 0;
}

子题1:摘花生

数字三角形的状态转移方程基本一致。

#include <cstdio>
#include <algorithm>const int N = 110;int w[N][N], f[N][N];int main()
{int T;scanf("%d", &T);while (T--) {int n, m;scanf("%d%d", &n, &m);for (int i = 1; i <= n; i++) {for (int j = 1; j <= m; j++) {scanf("%d", &w[i][j]);}}for (int i = 1; i <= n; i++) {for (int j = 1; j <= m; j++) {f[i][j] = std::max(f[i-1][j], f[i][j-1]) + w[i][j];}}printf("%d\n", f[n][m]);}return 0;
}

子题2:最低通行费

#include <cstdio>
#include <cstring>
#include <algorithm>const int N = 110;
int w[N][N], f[N][N];int main()
{int n;scanf("%d", &n);for (int i = 1; i <= n; i++) {for (int j = 1; j <= n; j++) {scanf("%d", &w[i][j]);}}memset(f, 0x3f, sizeof f);//摘花生的属性是max,因此f无需初始化,初始值为0可以在状态计算中自动覆盖,但本题的属性为min,f需初始化为∞f[1][1] = w[1][1];for (int i = 1; i <= n; i++) {for (int j = 1; j <= n; j++) {if (j == 1) f[i][j] = std::min(f[i][j], f[i-1][j] + w[i][j]);else if (i == 1) f[i][j] = std::min(f[i][j], f[i][j-1] + w[i][j]);//最左边一列和最上面一行的状态转移分别只能从上面和左面转移else {f[i][j] = std::min(f[i][j-1], f[i-1][j]) + w[i][j];}}}printf("%d", f[n][n]);return 0;
}

也可以这么写:

#include <cstdio>
#include <cstring>
#include <algorithm>const int N = 110;
int w[N][N], f[N][N];int main()
{int n;scanf("%d", &n);for (int i = 1; i <= n; i++) {for (int j = 1; j <= n; j++) {scanf("%d", &w[i][j]);}}memset(f, 0x3f, sizeof f);f[1][1] = w[1][1];for (int i = 1; i <= n; i++) {for (int j = 1; j <= n; j++) {f[i][j] = std::min(f[i][j], f[i-1][j] + w[i][j]);f[i][j] = std::min(f[i][j], f[i][j-1] + w[i][j]);}}printf("%d", f[n][n]);return 0;
}

m i n \rm min min 的括号里写f[i][j]是为了不特判起点f[1][1]。保险起见,建议特判一下起点,每次仔细思考边界和属性的关系。

子题3:方格取数

误区避雷:为什么不能分开两次走?(贪心
第一次走到 ( n , n ) (n, n) (n,n) 求出最大值并记录路径令路径上点收益为 0 0 0 后再走一次。
第一次走为局部最优并且也对第二次走造成了影响,第二次走是在第一次影响下所能走的局部最优,不具备“无后效性”,因此分开两次走并不是全局最优解。

换言之,第一次走的时候有可能有好几条路径都是第一次的解,而你分开走只能选择其中的一条。很不幸的是,第一次走过的地方会被重置成 0 0 0 ,而你不确定的是第一次同样是最优解而未被你选择的情况下第二次的值会不会比你已经得出的答案要大。

因此我们两条路同时走,从 ( 1 , 1 ) (1,1) (1,1) 走到 ( n , n ) (n,n) (n,n)。用 f [ i 1 ] [ j 1 ] [ i 2 ] [ j 2 ] f[i_1][j_1][i_2][j_2] f[i1][j1][i2][j2] 表示从 ( 1 , 1 ) (1,1) (1,1) ( 1 , 1 ) (1,1) (1,1) 走到 ( i 1 , j 1 ) (i_1,j_1) (i1,j1) ( i 2 , j 2 ) (i_2,j_2) (i2,j2) 能取得的最大的数字和。

由于两条路同时走,有 i 1 + j 1 = i 2 + j 2 i_1 + j_1 = i_2 + j_2 i1+j1=i2+j2。因此四维的 d p dp dp 可以被优化到三维。
k = i 1 + j 1 k = i_1 + j_1 k=i1+j1 f [ k ] [ i 1 ] [ i 2 ] f[k][i_1][i_2] f[k][i1][i2] 就表示 f [ i 1 ] [ j 1 ] [ i 2 ] [ j 2 ] f[i_1][j_1][i_2][j_2] f[i1][j1][i2][j2]

状态计算:
集合划分:看最后一步的操作。由于是分别从 ( 1 , 1 ) (1,1) (1,1) ( 1 , 1 ) (1,1) (1,1) 走到 ( i 1 , j 1 ) (i_1,j_1) (i1,j1) ( i 2 , j 2 ) (i_2,j_2) (i2,j2),而每条路的最后一步都可能是向右或向下,因此总共可以将集合分为 4 4 4 个子集。
① 最后一步分别是下、下: f [ k − 1 ] [ i 1 − 1 ] [ i 2 − 1 ] f[k-1][i_1 - 1][i_2 - 1] f[k1][i11][i21]
② 最后一步分别是右、下: f [ k − 1 ] [ i 1 ] [ i 2 − 1 ] f[k-1][i_1][i_2 - 1] f[k1][i1][i21]
③ 最后一步分别是下、右: f [ k − 1 ] [ i 1 − 1 ] [ i 2 ] f[k-1][i_1 - 1][i_2] f[k1][i11][i2]
④ 最后一步分别是右、右: f [ k − 1 ] [ i 1 ] [ i 2 ] f[k-1][i_1][i_2] f[k1][i1][i2]

i 1 = = i 2 i_1 == i_2 i1==i2 j 1 = = j 2 j_1 == j_2 j1==j2 的情况下,两点重合,只需加上 w [ i 1 ] [ j 1 ] w[i_1][j_1] w[i1][j1],否则的话还需加上 w [ i 2 ] [ j 2 ] w[i_2][j_2] w[i2][j2]

#include <cstdio>
#include <algorithm>const int N = 15;
int w[N][N], f[N * 2][N][N];int main()
{int n;scanf("%d", &n);int a, b, c;while (scanf("%d%d%d", &a, &b, &c) != EOF, a || b || c) w[a][b] = c;for (int k = 2; k <= n * 2; k++) {//起始时(1,1), k = 2for (int i1 = 1; i1 <= k; i1++) {for (int i2 = 1; i2 <= k; i2++) {int j1 = k - i1, j2 = k - i2;int t = w[i1][j1];if (i1 != i2) t += w[i2][j2];int& x = f[k][i1][i2];x = std::max(x, f[k - 1][i1 - 1][i2 - 1] + t);x = std::max(x, f[k - 1][i1 - 1][i2] + t);x = std::max(x, f[k - 1][i1][i2 - 1] + t);x = std::max(x, f[k - 1][i1][i2] + t);}}}printf("%d", f[n * 2][n][n]);return 0;
}

需要注意的一点是这里 i 1 i_1 i1 i 2 i_2 i2 的范围不可直接写 i1 <= ni2 <= n,这样在 k k k 较小的时候, j 1 j_1 j1 j 2 j_2 j2 就会小于 0 0 0,不符合题意。建议书写i1 <= n && i1 <= ki2 <= n && i2 <= k,包含了其固有的范围和题目要求的范围。

子题4:传纸条

首先需要明确的是,从右下角回传可以等价为从左上角传两次。

如果两个点相交,这个点的值只能加一次,然而我们肯定能找到一条绕过这个点走到下个点的路径,这条路径一定是大于之前相交路径的。

数学表达就是:两条路径在一个点,那么在这个点加的值就是 0 + g [ i , j ] 0+g[i,j] 0+g[i,j], 但是我们可以让其中一条路径绕过这个点再走到这个点的下一个点 那么加的值应该是 g [ i , j − 1 ] + g [ i , j ] g[i,j-1] + g[i,j] g[i,j1]+g[i,j]

因为是非负数,所以我们可以找到一条大于等于之前有相交点的路径,那么这个有相交点的一定不是最优解;即便这条路径是最优解也有另一条最优解和这个路径和一样,但是我们只需要输出路径和就可以了,最优解路径有可能是有相交点的,但是也有另一个最优解没有相交点,那么我们输出的路径和肯定可以是一条没有相交点的最优解。

以上就证明了,本题中“不允许走同一个点”的要求没用,最优解的情况与方格取数完全一致

#include <cstdio>
#include <algorithm>const int N = 55;
int w[N][N], f[N << 1][N][N];int main()
{int m, n;scanf("%d%d", &m, &n);for (int i = 1; i <= m; i++) {for (int j = 1; j <= n; j++) {scanf("%d", &w[i][j]);}}for (int k = 2; k <= n + m; k++) {for (int i1 = 1; i1 <= m && i1 < k; i1++) {for (int i2 = 1; i2 <= m && i2 < k; i2++) {int j1 = k - i1, j2 = k - i2;int& x = f[k][i1][i2];int t = w[i1][j1];if (i1 != i2) t += w[i2][j2];x = std::max(x, f[k - 1][i1 - 1][i2 - 1] + t);x = std::max(x, f[k - 1][i1 - 1][i2] + t);x = std::max(x, f[k - 1][i1][i2 - 1] + t);x = std::max(x, f[k - 1][i1][i2] + t);}}}printf("%d", f[n + m][m][m]);return 0;
}

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

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

相关文章

如何了解AI基础概念

1. **在线课程和教程&#xff1a;** - 寻找在线AI课程或教程&#xff0c;例如Coursera、edX、Udemy等平台上的课程。这些课程通常会从基础概念开始介绍&#xff0c;逐步深入。 2. **书籍阅读&#xff1a;** - 阅读与AI相关的书籍&#xff0c;如《Python深度学习》、《机…

[Rust] 使用vscode实现HelloWorld程序并进行debug

一、简介 本文介绍了如何使用vscode编写rust&#xff0c;实现打印"Hello, world!"的程序。 二、工具安装 0. 环境介绍&#xff1a; Linux &#xff08;或者windowswsl&#xff09; 1. 安装rust编译器rustc和包管理器cargo。 请参考连接&#xff1a;Rust 程序设…

vue集成百度地图,实现关键字搜索并自定义覆盖物,保存成静态图片

vue集成百度地图&#xff0c;实现关键字搜索并自定义覆盖物 index.html引入百度地图js <script type"text/javascript" src"https://api.map.baidu.com/api?v2.0&typewebgl&akxxxxxxwMprS7jIfPt354VdgP"></script>vue页面代码 <…

Android iOS客户端自动化UI自动化airtest从0到1搭建macos

一、基础环境 1. 安装jdk 选择jdk8 如果下载高版本 可能不匹配会失败 下载.dmg文件 苹果电脑 &#xff5c; macOS &#xff5c; jdk1.8 &#xff5c; 环境变量配置_jdk1.8 mac-CSDN博客 Java Downloads | Oracle jdk环境变量配置 找到java home qamac ~ % cd /Library/J…

K8s+Nacos实现应用的优雅上下线【生产实践】

文章目录 前言一、环境描述二、模拟请求报错三、配置优雅上下线1.修改nacos配置2.修改depolyment配置3.重新apply deployment后测试4.整体(下单)测试流程验证是否生效 四、期间遇到的问题 前言 我们在使用k8s部署应用的时候&#xff0c;虽然k8s是使用滚动升级的&#xff0c;先…

【AI生成文章】flutter ChangeNotifierProvider 实用场景举例

内容由Ai 大模型生成&#xff0c;不能完全保障真实 ChangeNotifierProvider 是 Flutter 中一个非常实用的工具&#xff0c;用于在应用程序中管理和传递状态。以下是一些实用的场景举例&#xff1a; 1. 用户信息管理 在应用程序中&#xff0c;用户信息&#xff08;如用户名、…

【Android】【Bluetooth Stack】蓝牙音乐协议分析之音频控制与信息加载(超详细)

1. 精讲蓝牙协议栈(Bluetooth Stack):SPP/A2DP/AVRCP/HFP/PBAP/IAP2/HID/MAP/OPP/PAN/GATTC/GATTS/HOGP等协议理论 2. 欢迎大家关注和订阅,【蓝牙协议栈】和【Android Bluetooth Stack】专栏会持续更新中.....敬请期待! 目录 1. 音乐信息加载 1.1 歌曲信息 1.1.1 key_c…

selenium自动化登录模块HTMLTestRunner测试报告

1.下载HTMLTestRunner.py放到python的Lib目录下&#xff0c;python3之后的&#xff0c;文件要修改以下内容&#xff1a; 第94行&#xff0c;将import StringIO修改成import io 第539行&#xff0c;将self.outputBuffer StringIO.StringIO()修改成self.outputBuffer io.Strin…

【微服务】微服务架构的最佳实践总结!

目录 推荐超级课程: Docker快速入门到精通Kubernetes入门到大师通关课AWS云服务快速入门实战构建和管理微服务是一项艰巨的任务。这是因为微服务就像多个并行的整体应用程序,它们都必须处于同步通信和并发运行时间。因此,在设计和构建它们时考虑并应用最佳实践非常重要。以下…

C++ —— 日期计算器

1. 头文件 #pragma once #include <iostream> using namespace std;class Date { public:Date(int year 1, int month 1, int day 1);int GetMonthDay();bool operator>(const Date& d) const;bool operator>(const Date& d)const;bool operator<(c…

单例模式:双重效验锁的懒汉实现方式

单例模式&#xff1a;双重效验锁的懒汉实现方式 单例模式饿汉模式实现懒汉模式实现改进1 (创建多个实例)改进2 (性能比较低)改进3 (volatile禁止指令重排序) 单例模式 单例模式顾名思义就是指实例化一个对象&#xff0c;通过把构造方法私有化来禁止创建实例。 饿汉模式实现 饿汉…

GPU算力池管理工具Determined AI部署与使用教程(2024.03)

1. 概念 1.1 什么是Determined&#xff1f; Determined AI 是一个全功能的深度学习平台&#xff0c;兼容 PyTorch 和 TensorFlow。它主要负责以下几个方面&#xff1a; 分布式训练&#xff1a;Determined AI 可以将训练工作负载分布在多个 GPU&#xff08;可能在多台计算机上…

大模型开发中使用prompt提示最佳实践

在大型模型开发中&#xff0c;使用prompt&#xff08;提示&#xff09;是一种指导模型生成所需输出的方法。以下是在使用prompt时的一些最佳实践&#xff1a; 1、明确的提示&#xff1a;确保prompt提供了明确、清晰的指导&#xff0c;以便模型理解所需生成的内容。避免模棱两可…

IPP-7010 表面贴装 90 度混合耦合器

IPP-7010 表面贴装 90 度混合耦合器 IPP-7010 是一款表面贴装 90 度混合耦合器&#xff0c;工作频率为 800 至 2500 MHz&#xff08;0.8 至 2.5 GHz&#xff09;&#xff0c;平均额定功率为 200 瓦。IPP-7010 采用 0.40 x 1.80 英寸表面贴装封装。IPP-7010的幅度平衡小于0.6dB&…

鸿蒙预览报错 Only files in a module can be previewed

HarmonyOS第一课下载的源码无法运行&#xff0c;也无法预览&#xff0c;报错如题。 解决&#xff1a; 1、在预览页如“index.ets”文件下预览。 2、如果在通知栏看到如图提示&#xff0c;可看出是ohos/hvigor-ohos-plugin插件版本的问题&#xff0c;可点击蓝色解决方案同步并导…

HTTP Header Fields

HTTP&#xff08;超文本传输协议&#xff09;中包含多种类型的头部字段&#xff08;Header Fields&#xff09;&#xff0c;以下是常见的HTTP头部字段及其作用&#xff1a; ### 通用头字段&#xff08;General Header Fields&#xff09; - **Cache-Control**: 控制缓存行为&a…

python 函数(解包**、互相调用、作用域、函数的封装、内置函数:eval()、zip()、文件处理open())

函数解包 """ 1、函数的注释&#xff1a;参数和返回值 在注释里可以自动添加显示&#xff0c;只需手动加说明。2、函数的解包【拆包】&#xff1a;函数的参数要传递数据有多个值的时候&#xff0c;中间步骤拿到数据 保存在元组或者列表 或者字典里。 - 传递参数…

活用 C语言之union的精妙之用

一、union的基本定义 Union的中文叫法又被称为共用体、联合或者联合体。它的定义方式与结构体相同,但意义却与结构体完全不同。下面是union的定义格式: union 共用体名 {成员列表}共用体变量名;它与结构体的定义方式相同,但区别在于共用体中的成员的起始地址都是相同的,…

【理解机器学习算法】之Clustering算法(DBSCAN)

DBSCAN&#xff08;基于密度的空间聚类应用噪声&#xff09;是数据挖掘和机器学习中一个流行的聚类算法。与K-Means这样的划分方法不同&#xff0c;DBSCAN特别擅长于识别数据集中各种形状和大小的聚类&#xff0c;包括存在噪声和离群点的情况。 以下是DBSCAN工作原理的概述&am…

centos7 安装php82

安装epel扩展源 yum -y install epel-release vim 镜像地址: https://mirrors.aliyun.com/remi/ #这个阿里源专门提供了php的各种版本 下载镜像&#xff08;如果epel扩展源包含php8.2版本&#xff0c;可不需要下载此镜像。建议下载&#xff0c;百利无一害&#xff09; …