解题思路1:
- 首先搞清楚几个问题,到结尾点的最长上升子串应该思考成为,不是以结尾下表i结尾的子串。但是需要知道以i结尾的子串的长度。最终只要取出其中以某个点为结尾的最大长度子串就是要求的答案。
- 递归的思维方式来解决此题,求以最后结尾点i为结束的子串长度dp[i]只要比较节点0~i−1中是否存在v[i]>v[j] (其中0<=j<=i−1),利用mx记录符合条件的最大长度,如果存在即当前点为结尾的最大长度dp[i]=mx+1。
- 最后遍历整个数组。取出最大值输出
#include<bits/stdc++.h>
using namespace std;
int solve( int i, vector<int> &v, vector<int> &dp){//递归的写法
if(dp[i] != 0) return dp[i];
if (i == 0) dp[i] = 1;
else{
int mx = 0;
for(int j = 0; j < i; ++j){
int tmp = solve(j, v, dp);//坑点,此处必须先有值,再比较,否则就挂了。吐血的坑写法:if(v[i] > v[j]) mx = max(solve(j, v, dp), mx);
if(v[i] > v[j]) mx = max(tmp, mx);
}
dp[i] = mx + 1;
}
return dp[i];
}
void solve2(int n, vector<int> &v, vector<int> &dp){//递推的写法
dp[0] = 1;
for(int i = 0; i < n; ++i){
int mx = 0;
for(int j = 0; j < i; ++j){
if (v[i] > v[j])
mx = max(dp[j], mx);
}
dp[i] = mx+1;
}
}
int main(){
int n;
cin>>n;
vector<int> arr(n);
for(int i = 0; i < n; ++i){
cin>>arr[i];
}
vector<int> dp(n);
solve(n-1, arr, dp);
// solve2(n, arr, dp);
int res = 0;
for(auto i : dp){
res = max(res, i);
}
cout<<res<<endl;
return 0;
}
解题思路2:
- 上述解法时间复杂度O(n2),在数据量超过105的时候就不够快了。
- 这题可以有O(nlogn)做法
- 领dp表示arr0≤i<n的最大上升子序列,重点考虑如何去维护这个dp,以及如何去求得其长度len。
- 初始化:dp0=arr0,len=1
- 对于后续i(1≤i≤n−1),使用二分查找到arri>=dpj(0≤j≤n),如果arri>dpcurrent_end,直接在尾部插入arri,否则找到第一个≥arri的位置将arri写入即可(可以理解为找到位置插入以后丢弃其之后的所有元素。
#include<bits/stdc++.h>
using namespace std;
int main(){
int n;
cin>>n;
vector<int> arr(n);
vector<int> dp(n+1, INT_MAX);
for(int i = 0; i < n; ++i){
cin>> arr[i];
//在dp数组中找到>= arr[i]的位置,讲arr[i]写入忽略其后部分,因为其前部分一定和它构成上升子序列。
dp[lower_bound(dp.begin(), dp.end(), arr[i])-dp.begin()] = arr[i];
}
int ans = 0;
while(dp[ans] != INT_MAX) ans ++;
cout<<ans;
return 0;
}