最长上升子序列的变种,即线性dp
目录
1.拦截导弹
这题通过一个数学定理将问题转化成了求最长递增子序列的问题。
首先,第一问很明显就是求最长非递减子序列,第二问要用一个定理,可以参考:
Dilworth定理是个啥东东_litble的博客-CSDN博客_dilworth定理
最后转化了问题后,求解就显得十分的简单,数学yyds,代码如下所示:
#include<bits/stdc++.h>
using namespace std;
// 求最长非递减子序列长度
int solve1(vector<int> &height){
int len=height.size();
int res=INT_MIN;
vector<int> dp(len, 1);
for(int i=0;i<len;++i){
for(int j=0;j<i;++j){
if(height[i] <= height[j]){
dp[i]=max(dp[i], dp[j]+1);
}
}
res=max(res, dp[i]);
}
return res;
}
// 求最长递增子序列长度
int solve2(vector<int> &height){
int len=height.size();
int res=INT_MIN;
vector<int> dp(len, 1);
for(int i=0;i<len;++i){
for(int j=0;j<i;++j){
if(height[i] > height[j]){
dp[i]=max(dp[i], dp[j]+1);
}
}
res=max(res, dp[i]);
}
return res;
}
int main(){
int n;
cin>>n;
vector<int> height(n);
for(int i=0;i<n;++i){
cin>>height[i];
}
int res1=solve1(height);
int res2=solve2(height);
cout<<res1<<endl<<res2<<endl;
return 0;
}
2.合唱队形
求最少需要几个同学出列,其实可以想到求最长序列长度。
既然是先递增后递减的一个序列格式,我们考虑求取这种序列的最长长度,求取可以将序列拆开,先从左向右求取每个以当前字符结束的最长子序列长度dpl[i],再从右向左求一遍最大长度dpr[i],那么很容易想到,以i为中心的序列的长度是dpl[i]+dpr[i]-1。
至于初始化,单个数字就可以满足条件,所以初始化的时候都为1。
代码如下所示:
#include<bits/stdc++.h>
using namespace std;
int main(){
int n;
cin>>n;
vector<int> height(n);
for(int i=0;i<n;++i){
cin>>height[i];
}
vector<int> dpl(n, 1);
for(int i=0;i<n;++i){
for(int j=0;j<i;++j){
if(height[i]>height[j]){
dpl[i]=max(dpl[i], dpl[j]+1);
}
}
}
vector<int> dpr(n, 1);
for(int i=n-1;i>=0;--i){
for(int j=n-1;j>i;--j){
if(height[i]>height[j]){
dpr[i]=max(dpr[i], dpr[j]+1);
}
}
}
int res=INT_MIN;
for(int i=0;i<n;++i){
res=max(res, dpl[i]+dpr[i]-1);
}
cout<<n-res<<endl;
return 0;
}
3.信箱嵌套
这里的最长上升子序列其实就是换成了(a,b)这个二元序的最长上升子序列,我们首先利用sort进行排序,这样保证第一维是大于等于前面的,在此基础上进行两个序列对的比较。这样我们可以求出最长上升序列。
代码如下所示:
#include<bits/stdc++.h>
using namespace std;
typedef pair<int, int> PII;
bool cmp(PII &a, PII &b){
return a.first>b.first && a.second>b.second;
}
int solve1(vector<PII> &v, vector<int> &dp, int n){
if(dp[n]!=0) return dp[n];
if(n==0) dp[n]=1;
else{
for(int i=0;i<n;++i){
int tmp=solve1(v, dp, i);
if(cmp(v[n], v[i])){
dp[n]=max(dp[n], tmp+1);
}
}
}
return dp[n];
}
void solve2(vector<PII> &v, vector<int> &dp){
dp[0]=1;
int i,j;
int res=INT_MIN;
for(int i=1;i<v.size();++i){
for(int j=0;j<i;++j){
if(cmp(v[i], v[j])){
dp[i]=max(dp[i], dp[j]);
}
}
dp[i]+=1;
res=max(res, dp[i]);
}
}
int main(){
int n;
cin>>n;
vector<PII> v(n);
for(int i=0;i<n;++i){
cin>>v[i].first;
cin>>v[i].second;
}
vector<int> dp(n);
sort(v.begin(), v.end());
// solve1(v, dp, n-1);
solve2(v, dp);
int res=0;
for(int i=0;i<n;++i){
res=max(res, dp[i]);
}
cout<<res<<endl;
return 0;
}
这里一个比较坑的点是每次要先取出最大值,之后再加一,跑跑下面这个案例就知道了
11
681 578
643 780
548 873
850 187
28 476
531 578
559 995
419 130
638 297
988 125
267 52