洛谷P3902题解析:动态规划求解最长递增子序列(时间复杂度优化)
一、题目解读
洛谷P3902题要求给定一个整数序列,计算将其转换为最长递增子序列所需的最小修改次数。题目核心在于寻找原序列与最长递增子序列之间的差异,通过算法优化求解效率。理解“修改次数”即原序列长度与最长递增子序列长度的差值,成为解题的关键突破口。
二、解题思路
采用动态规划(DP)与二分查找的结合策略。传统LIS(最长递增子序列)问题通常使用O(n^2)的动态规划,但此处通过维护一个递增序列dp,利用lower_bound函数将时间复杂度降至O(nlogn)。核心思想是:每次遍历原序列元素时,找到dp中第一个大于等于该元素的位置,替换或扩展dp序列,最终dp的长度即为LIS长度。
三、解题步骤
1. 输入与初始化:读取序列n及元素,初始化动态规划数组dp为空。
2. 循环遍历:对每个元素num,通过二分查找找到dp中第一个不小于num的位置it。
3. 替换/扩展:
○ 若it为dp末尾(表示num大于所有现有元素),则扩展dp:dp.push_back(num)。
○ 否则替换it位置元素:*it = num(保证dp始终递增且长度最短)。
4. 输出结果:最终修改次数为n - dp.size(),即原序列长度减去LIS长度。
四、代码及注释
#include <bits/stdC++.h> using namespace std; const int MAXN = 1e5+5; int main() { ios::sync_with_stdio(false); // 加快输入/输出速度 cin.tie(0); int n; cin >> n; // 输入序列长度 vector<int> nums(n); // 存储原序列 for(int i=0; i<n; i++) { cin >> nums[i]; } // 计算最长递增子序列长度 vector<int> dp; // 动态规划数组,存储递增子序列 for(int num : nums) { // 使用lower_bound找到第一个不小于当前元素的位置 auto it = lower_bound(dp.begin(), dp.end(), num); if(it == dp.end()) { // 若num大于所有dp元素,扩展序列 dp.push_back(num); } else { // 替换第一个不小于num的元素(缩短dp长度) *it = num; } } // 需要修改的次数 = 总长度 - 最长递增子序列长度 cout << n - dp.size() << endl; return 0; }
五、总结
本解法通过动态规划与二分查找的结合,巧妙地将LIS问题的时间复杂度优化至线性对数级别。关键在于维护递增序列dp并利用lower_bound精准定位替换位置,避免重复元素,从而高效计算修改次数。此思路对处理大规模数据的长递增子序列问题具有重要参考价值,同时为算法优化提供了实践范例。
原创内容 转载请注明出处