洛谷B3637 最长上升子序列|线性DP、模板题

本文讲述了如何使用动态规划解决B3637最长上升子序列问题,强调了初始化dp数组的重要性,以及连续和非连续子序列的不同处理方法。作者分享了两种代码实现,一种针对连续子序列,另一种考虑了数列中每个数字的最大长度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目:

B3637 最长上升子序列 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

 注意点:

学习:https://blog.csdn.net/weixin_72060925/article/details/128425930

虽然这道题已经见过很多类似的变形了,但是还是犯迷糊。

1.首先,没有初始化每个dp元素,误以为总有一个    dp[i]=max(dp[i],dp[j]+1);会把当前的dp赋值为1,然而如果前面的数都大于这个数,这个dp依旧是0,所以需要手动初始化为1。

2.忘记了还要在每个dp元素里再找一个最大的。因为不确定最长递增子序列以哪个元素结尾,但是一定以某个元素结尾,求出了以每个元素结尾的最长长度,答案在里面选即可。

3.还考虑r[i]表示数列里出现的数字i对应的最大长度,配合上dp[i]表示以i结尾的最长长度(那么dp[i]就等于r[小于a[i]的所有数]中的最大长度+1),但是数的范围达到了1e6,满足递增的话,还是要一个一个从a[i]-1去遍历到0,时间复杂度太高,不行。类似蓝桥杯的接龙序列,只有10个情况比较适合这样做,而且那道题因为是头尾相同可以直接找到,不用一一遍历。

蓝桥杯23年第十四届省赛-接龙数列|DFS、线性DP-CSDN博客

再总结一下类似的套路:

对于子序列问题,可以构造一个dp[i]数组,表示以第i个元素结尾的目标值(如最长长度),如果是连续的子序列,从dp[i-1]递推来就可以;如果子序列不要求连续元素,那么就要从前i-1项dp递推来,比较出目标值。最后,在所有的dp中挑出一个值作为答案。

 

代码:

#include <bits/stdc++.h>

using namespace std;
const int N=1e6+10;
const int M=5e3+10;
//以某个数字结尾的最长子序列
int dp[M],r[N];

int a[M];
int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	int n;
	cin>>n;
	
	for(int i=0;i<n;++i){
		cin>>a[i];
	}
    int ans=0;
    dp[0]=1;
    
    for(int i=1;i<n;++i){
    	//要初始化为1,因为可能存在前面都比它大的情况 ,就不会进入下面的dp赋值的语句 
        dp[i]=1;
    	for(int j=i-1;j>=0;j--){
    		if(a[j]<a[i]){
    			dp[i]=max(dp[i],dp[j]+1);
			}
		}
		
		//还需要在所有以第i个数为结尾的 最长长度里选一个最长的 
		if(dp[i]>ans) ans=dp[i];
	}

   cout<<ans;
   return 0;
}

注意点第3点考虑的方法,r[i]表示数列里出现的数字i对应的最大长度:

#include <bits/stdc++.h>

using namespace std;
const int N=1e6+10;
const int M=5e3+10;
//以某个数字结尾的最长子序列
int dp[M],r[N];

int a[M];
int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	int n;
	cin>>n;
	
	for(int i=0;i<n;++i){
		cin>>a[i];
	}
    int ans=0;
    dp[0]=1;r[a[0]]=1; 
    
//    for(int i=1;i<n;++i){
//    	//要初始化为1,因为可能存在前面都比它大的情况 ,就不会进入下面的dp赋值的语句 
//        dp[i]=1;
//    	for(int j=i-1;j>=0;j--){
//    		if(a[j]<a[i]){
//    			dp[i]=max(dp[i],dp[j]+1);
//			}
//		}
//		
//		//还需要在所有以第i个数为结尾的 最长长度里选一个最长的 
//		if(dp[i]>ans) ans=dp[i];
//	}

    for(int i=1;i<n;++i){
    	//要初始化为1,因为可能存在前面都比它大的情况 ,就不会进入下面的dp赋值的语句 
        dp[i]=1;
    	for(int j=a[i]-1;j>=0;j--){
    		dp[i]=max(r[j]+1,dp[i]);
		}
		
		r[a[i]]=max(r[a[i]],dp[i]);
		//还需要在所有以第i个数为结尾的 最长长度里选一个最长的 
		if(dp[i]>ans) ans=dp[i];
	}
	
	
   cout<<ans;
   return 0;
}

<think>嗯,用户想了解蓝桥杯C++考试的考点和解析。首先,我需要回想一下蓝桥杯的常见考点,可能包括基础语法、数据结构、算法等。用户提到的是大学C组,可能难度适中,但具体内容需要确认。 我记得蓝桥杯常考的有循环、条件语句、数组、字符串处理这些基础内容。比如质数判断、斐波那契数列这些经典题目。然后数据结构方面,栈、队列、链表、树、图这些应该都是重点,特别是树的遍历和图的最短路径,比如DFS、BFS、Dijkstra算法。 接下来是算法部分,排序算法如快速排序、归并排序,还有贪心、动态规划、回溯这些。动态规划可能会考背包问题,最长子序列之类的。数学题方面,最大公约数、快速幂、排列组合这些也是常出现的。 STL库的应用也很重要,比如vector、map、set的使用,还有字符串处理的函数。另外,文件操作和输入输出处理也不能忽视,特别是大数处理和高精度计算,蓝桥杯有时候会有这类题目。 然后,我需要参考用户提供的引用,里面提到的是个人题解和思路,可能存在一些错误,但可以作为参考。例如,引用中提到动态规划在2024年考试中的重要性,可能需要强调这一点。 另外,用户可能想知道如何高效备考,比如推荐书籍或在线资源。常见的书籍有《算法竞赛入门经典》,在线平台如、LeetCode。还有注意时间复杂度的优化,避免超时,以及代码调试技巧。 可能还需要提醒用户注意蓝桥杯的评分标准,部分分和优化策略。比如,即使无法完全解决题目,也要尽量写出能拿部分分的代码。 最后,确保回答结构清晰,分点列出考点,每个考点下给出例子和注意事项,并附上相关问题帮助用户深入思考。同时,检查是否符合格式要求,数学公式用$...$,独立公式用$$...$$,正确使用LaTeX语法,引用标识自然添加。</think>### 蓝桥杯 C++ 考试重点与常见考点解析 #### 一、基础语法与编程能力 1. **输入输出与数据类型** - 蓝桥杯常涉及大整数运算(如 $10^{18}$ 范围)和高精度计算,需熟练使用 `long long`、字符串处理或自定义高精度结构。 - 示例:`scanf("%lld", &n)` 处理长整型输入;字符串反转实现高精度加法。 2. **循环与条件语句** - 嵌套循环常出现在质数筛法、矩阵操作等场景,例如埃拉托斯特尼筛法求质数: ```cpp for (int i=2; i*i<=n; i++) if (!st[i]) for (int j=i*i; j<=n; j+=i) st[j]=true; ``` 3. **函数与递归** - 递归应用广泛,如斐波那契数列、全排列生成。需注意递归深度限制(蓝桥杯栈空间默认约 1MB)。 --- #### 二、数据结构与算法核心考点 1. **线性数据结构** - **数组/字符串**:前缀和、差分数组(区间修改优化)。 - **链表**:双向链表实现 LRU 缓存(近年真题)。 - **栈/队列**:单调栈解决“下一个更大元素”问题,队列用于 BFS。 2. **树与图论** - **二叉树**:遍历(先序/中序/后序)、最近公共祖先(LCA)。 - **图论**:最短路径(Dijkstra、Floyd)、最小生成树(Kruskal)。 示例代码(Dijkstra 核心逻辑): ```cpp priority_queue<pair<int,int>, vector<pair<int,int>>, greater<>> pq; pq.emplace(0, start); while (!pq.empty()) { auto [dist, u] = pq.top(); pq.pop(); if (visited[u]) continue; visited[u] = true; for (auto [v, w] : graph[u]) { if (dist + w < dis[v]) { dis[v] = dist + w; pq.emplace(dis[v], v); } } } ``` 3. **动态规划(重点!)** - **经典模型**:背包问题(01背包、完全背包)、最长公共子序列(LCS)。 - **优化技巧**:状态压缩(如滚动数组)、斜率优化。 - 真题示例:2024年C组题目中出现“分割数组使子数组和差值最小”,需结合前缀和与 DP 状态设计[^1]。 --- #### 三、数学与计算思维 1. **数论基础** - 快速幂算法(计算 $a^b \mod p$,时间复杂度 $O(\log b)$): ```cpp long long qpow(long long a, long long b, long long p) { long long res = 1; while (b) { if (b & 1) res = res * a % p; a = a * a % p; b >>= 1; } return res; } ``` - 扩展欧几里得算法线性同余方程。 2. **组合数学** - 排列组合公式(如 $C(n,k) = \frac{n!}{k!(n-k)!}$)的实际应用,常需配合动态规划或预处理阶乘逆元。 --- #### 四、实战技巧与备赛建议 1. **代码模板准备** - 提前整理高频算法模板(如并查集、快速排序),考试时直接修改参数适配题目。 2. **调试与优化** - 使用 `freopen("input.txt", "r", stdin)` 重定向输入测试用例。 - 避免超时:优先使用 $O(n \log n)$ 算法(如排序代替双重循环)。 3. **真题训练方向** - 参考近三年C组真题,重点练习“模拟题”(如日期计算、图形输出)和“贪心+动态规划”组合题。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

CGuts350

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值