动态规划 -- #线性DP 数字三角形(C++)


线性dp

状态的转移具有线性递推关系,每个状态只依赖之前的状态,按照线性顺序一步步递推下去。

例题

给定一个如下图所示的数字三角形,从顶部出发,在每一结点可以选择移动至其左下方的结点或移动至其右下方的结点,一直走到底层,要求找出一条路径,使路径上的数字的和最大。

截自Acwing

思路

通过观察可以得到规律,每一个位置上的路径和可以是其左上方的元素走来或者是右上方的元素走来,而题目要求我们求最值,我们只需要求左上方和右上方哪里过来的max即可
注意: 第一列元素的左上方是空的,最后一列右上方是空的
所以我们待会初始化dp数组的时候要前后多初始化一个

在这里插入图片描述

状态表示

f[i][j]表示所有从起点走到(i,j)的路径最大

状态计算

  • 从左上方走来 f[i][j]=f[i-1][j]
  • 从右上方走来 f[i][j]=f[i-1][j-1]
  • 最后求max f[i][j]=max(f[i-1][j],f[i-1][j-1])

完整代码

#include<iostream>
#include<algorithm>

using namespace std;

const int N = 550;
int dp[N][N], a[N][N];
//当三角形中所有数据都为-10000且有500层的时候
//这时候答案应该为-5000000
#define INF -5000010//最后面解释为什么


int main() {
    int n;
    cin >> n;
    //初始化dp数组,这里前后要多算一个
    for(int i=0; i<=n+1; i++){
        for(int j=0; j<=i+1; j++){
            dp[i][j]=INF;
        }
    }
    //录入原数据
    for(int i=1; i<=n; i++){
        for(int j=1; j<=i; j++){
            cin>>a[i][j];
        }
    }
    //dp[1][1]不能用公式算,我们直接手动赋初始值
    dp[1][1]=a[1][1];
    
    for(int i=2; i<=n; i++){
        for(int j=1; j<=i; j++){
            dp[i][j]=max(dp[i-1][j-1],dp[i-1][j])+a[i][j];
        }
    }
    //定义INF为-5000010的原因就是这里要MAX运算最后一行看看哪一列有最大值
    int res=INF;
    for(int i=1; i<=n; i++)res=max(res,dp[n][i]);
    cout<<res;
    
	return 0;
}

结语


🌻编写本篇文章目的是笔者想以输出的形式进行学习,顺便记录学习点滴🌻

🌹 如果本篇文章对你有帮助的话那就点个赞吧👍🌹

😇 本篇文章存在多处不足,如有修改意见,可以私信或者评论哦,还望海涵 😇


在这里插入图片描述

<think>根据用户需求,用户需要一份适用于C++ CSP-J复赛的线性动态规划线性DP)模板代码,并且要求带有详细注释。考虑到CSP-J复赛的难度,线性DP问题通常包括一维或二维的线性序列问题(如最大子段和、最长上升子序列、简单路径问题等),代码应简洁易懂,符合竞赛要求。 我们将提供一个经典的一维线性DP模板:最大子段和问题(最大连续子序列和)的代码,并带有详细注释。同时,为了展示通用性,我们再提供一个二维线性DP的模板:数字三角形问题。这两个问题都是CSP-J复赛中常见的题型。 注意:由于CSP-J复赛中动态规划题目通常不会过于复杂,因此我们选择的模板都是基础且经典的。 模板1:最大子段和(一维线性DP) 问题描述:给定一个整数数组,求连续子序列的最大和。 模板2:数字三角形(二维线性DP) 问题描述:给定一个数字三角形,从顶部到底部,每次只能向下或向右下移动,求从顶部到底部的最大路径和。 我们将分别给出这两个问题的代码,并附上详细注释。 </think> 以下是适用于C++ CSP-J复赛的线性动态规划线性DP)模板代码,包含详细注释和经典例题实现: ### 一、一维线性DP模板(最大子段和问题) ```cpp #include <iostream> #include <algorithm> #include <climits> using namespace std; const int MAXN = 1005; // 最大数组长度 int main() { int n; cin >> n; int nums[MAXN]; // 存储输入序列 int dp[MAXN]; // dp[i]表示以第i个元素结尾的最大子段和 // 输入数据 for (int i = 1; i <= n; i++) { cin >> nums[i]; } // 初始化边界条件 dp[1] = nums[1]; // 第一个元素的最大子段和就是它本身 int ans = dp[1]; // 记录全局最大值 // 核心DP递推 for (int i = 2; i <= n; i++) { /* 状态转移方程: 选择1:只包含当前元素 (nums[i]) 选择2:当前元素 + 前一个位置的子段和 (dp[i-1] + nums[i]) */ dp[i] = max(nums[i], dp[i-1] + nums[i]); // 更新全局最大值 ans = max(ans, dp[i]); } cout << "最大子段和为:" << ans << endl; return 0; } ``` #### 代码说明: 1. **状态定义**:$dp[i]$ 表示以第 $i$ 个元素结尾的连续子数组的最大和 2. **转移方程**:$dp[i] = \max(nums[i],\ dp[i-1] + nums[i])$ 3. **边界条件**:$dp[1] = nums[1]$ 4. **时间复杂度**:$O(n)$ 5. **空间复杂度**:$O(n)$ 6. **典型应用**:最大连续子序列和、最长递增子序列变体 --- ### 二、二维线性DP模板(数字三角形问题) ```cpp #include <iostream> #include <algorithm> using namespace std; const int MAXN = 105; // 三角形最大行数 int main() { int n; cin >> n; int tri[MAXN][MAXN]; // 存储数字三角形 int dp[MAXN][MAXN]; // dp[i][j]表示从顶部到(i,j)的最大路径和 // 输入数据 for (int i = 1; i <= n; i++) { for (int j = 1; j <= i; j++) { cin >> tri[i][j]; } } // 初始化边界条件(顶部) dp[1][1] = tri[1][1]; // 自顶向下递推 for (int i = 2; i <= n; i++) { for (int j = 1; j <= i; j++) { /* 状态转移方程: 从左上角转移:dp[i-1][j-1](当j>1时) 从正上方转移:dp[i-1][j](当j<i时) 注意边界处理 */ int left = (j > 1) ? dp[i-1][j-1] : 0; // 左上角 int up = (j < i) ? dp[i-1][j] : 0; // 正上方 // 取两条路径中的较大值 dp[i][j] = max(left, up) + tri[i][j]; } } // 在最后一行寻找最大值 int ans = 0; for (int j = 1; j <= n; j++) { ans = max(ans, dp[n][j]); } cout << "最大路径和为:" << ans << endl; return 0; } ``` #### 代码说明: 1. **状态定义**:$dp[i][j]$ 表示从顶点到位置 $(i,j)$ 的最大路径和 2. **转移方程**:$dp[i][j] = \max(dp[i-1][j-1], dp[i-1][j]) + tri[i][j]$(需处理边界) 3. **边界条件**:$dp[1][1] = tri[1][1]$ 4. **时间复杂度**:$O(n^2)$ 5. **空间优化**:可使用滚动数组将空间复杂度优化到 $O(n)$ 6. **典型应用**:数字三角形、最小路径和、网格路径问题 --- ### 关键知识点总结: 1. **DP三要素**: - 状态定义:明确定义 $dp$ 数组的含义 - 状态转移:建立子问题间的关系(核心方程) - 边界处理:初始状态和特殊位置处理 2. **解题技巧**: ```cpp // 滚动数组优化示例(数字三角形空间优化) int dp[2][MAXN]; // 仅用两行存储 for (int i = 1; i <= n; i++) { for (int j = 1; j <= i; j++) { dp[i%2][j] = max(dp[(i-1)%2][j-1], dp[(i-1)%2][j]) + tri[i][j]; } } ``` 3. **常见错误规避**: - 忘记初始化边界条件 - 数组越界访问 - 状态转移方程遗漏情况 - 未考虑负数情况(如最大子段和需支持负数) > 在CSP-J复赛中,线性DP常考题型包括:最大子段和、最长上升子序列(LIS)、简单背包问题、网格路径问题等。建议结合真题进行针对性训练[^1][^2]。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值