最长上升子序列是动态规划的中比较常见的题目,我第一次听最长上升子序列,浙大陈越姥姥的课程中听到的。她分析了用不同的方法去解决该问题。
题目如下:
Acwing-895
Leetcode-300
这两道题目都是最长上升子序列的题目:很经典
按照以往的惯例,还是去用闫氏DP分析法去分析:
状态表示f[i]:
如何理解状态表示呢?能用一维度解决,坚决不使用二维。毕竟还是要从实际问题除法
集合:f[i]是状态数组,表示的是:以i结尾的最长上升子序列的长度
属性:以i结尾的最长上升自序里的长度的最大值
状态计算:
这里就是集合的划分:
这里先用口述去说明一下:如何从字面上去理解。
如果我的一段序列是,单调递减的:那么显然长度只有1,也就是固定到第i位置的单个元素的长度
比如:3 2 1.那么如果第i个位置(其实就是第三个位置),那么显然长度只有i啊
如果我的一段序列是,单调递增的:那么说明,以i结尾的状态数组的前面,存在一个索引位置j,使得arr[j]<arr[i],那么长度就要增加一位。还是要从全局除法,我们的目的是:讲一个大的问题分成若干个子问题,那我现在就分了啊,我将f[i]分成了f[j]+1.f[j]就是我们所有求子问题,f[j]再去求出以j结尾得最长上升子序列的最大值。
那么我们从字面上可以得出状态方程为:dp[i]=max(dp[i],d[j]+1)
这里画一个集合状态图去进行考虑:
补充一句,我们应该从整体上考虑,只要在集合上不重不漏,那么应该是正确的。
第一题代码如下:
#include <iostream>
#include <algorithm>
using namespace std;
const int N=1010;
int arr[N],dp[N];
int main(){
int n;
cin>>n;
for(int i=0;i<n;i++) cin>>arr[i];
for(int i=0;i<n;i++){
dp[i]=1;//以第i结尾的最长上升子序列
for(int j=0;j<i;j++){
if(arr[j]<arr[i]) dp[i]=max(dp[i],dp[j]+1);
}
}
int res=0;
for(int i=1;i<=n;i++) res=max(res,dp[i]);
cout<<res;
return 0;
}
第二题代码如下(其实是一摸一样的题目),但是力扣的形式是写一个函数:
这里引入一个小技巧,求一个数组最大值的时候,可以直接调用库函数,省心省力。
class Solution {
public:
int lengthOfLIS(vector<int>& nums) {
int len=nums.size();
if(len==0) return 0;
auto f=vector<int>(len,1);//状态数组,初始化的时候全部初始化为1
//边界处理
for(int i=1;i<len;i++){
for(int j=0;j<i;j++){
if(nums[j]<nums[i]) f[i]=max(f[i],f[j]+1);
}
}
//c11直接库函数返回数组元素的最大值
return *max_element(f.begin(),f.end());
}
};