最长上升(不下降)子序列(LIS) 不同求解方法(动规、贪心)
给定一个序列,求出它的最长上升子序列或者是最长不下降子序列的长度 或者输出这个子序列
一、动态规划 O(n^2)
1.求长度
首先来讨论最长上升子序列的情况,即子序列是严格上升的
假如我们以dp[i]表示以a[i]为结尾的上升子序列的长度 那么对于 j (1<=j<i),如果a[j]<a[i],很显然:
dp[i] = max(dp[i], dp[j]+1);
当然先要赋初值,dp[i]=1,因为一开始只有它自己组成这个序列
最后每次计算出dp[i]之后都要对答案进行更新
至于最长不下降子序列,只需要把a[j]<a[i]改为a[j]<=a[i]即可
代码:
#include <bits/stdc++.h> using namespace std; int anss=0,i,n,j,a[10005],dp[10005]; int main() { cin>>n; for(i=0;i<n;i++) { cin>>a[i]; } for(i=0;i<n;i++) { dp[i]=1; for(j=0;j<i;j++) { if(a[j]<a[i]) //如果是最长不下降子序列就改为 if(a[j]<=a[i]) { dp[i]=max(dp[j]+1,dp[i]); } } if(dp[i]>anss) anss=dp[i]; } cout<<anss<<endl; }
2.求序列
只需要从dp数组向前遍历,找到dp[i]==anss的之后再找dp[i]==anss-1的....以此类推即可
#include <bits/stdc++.h> using namespace std; int anss=0,i,n,j,a[10005],dp[10005]; int main() { cin>>n; for(i=0;i<n;i++) { cin>>a[i]; } for(i=0;i<n;i++) { dp[i]=1; for(j=0;j<i;j++) { if(a[j]<a[i]) { dp[i]=max(dp[j]+1,dp[i]); } } if(dp[i]>anss) anss=dp[i]; } cout<<anss<<endl; }
不过通常最长上升子序列的解是不唯一的,长度是唯一的。
二、贪心 O(nlogn)
对于一个上升子序列,显然其结尾元素越小,越有利于在后面接其他的元素,也就越可能变得更长。
因此,我们只需要维护 dp 数组(虽然已经不是动态规划了),对于每一个a [ i ],如果a [ i ]能接到 LIS 后面,就接上去;否则,就用 a [ i ] 取更新 dp数组:在dp数组中找到第一个大于等于a [ i ]的元素dp[ j ],用a [ i ]去更新dp [ j ]。怎么找到第一个大于等于的元素呢?只需要使用lower_bound()函数即可。
至于最长不下降子序列,只需要将 “在dp数组中找到第一个大于等于a [ i ]的元素dp[ j ]” 这一步改为找到第一个大于的元素即可。同样的将lower_bound换成upper_bound()
1.代码
#include <bits/stdc++.h> using namespace std; int anss=0,i,n,j,a[10005],dp[10005],temp; int main() { cin>>n; for(i=0;i<n;i++) { cin>>a[i]; dp[i]=0x7ffffff; } for(i=0;i<n;i++) { temp=lower_bound(dp,dp+n,a[i])-dp; if(temp+1>anss) { anss++; } dp[temp]=a[i]; } cout<<anss<<endl; }
2.求序列
只需要另外开一个数组b,记录dp[i]的位置,然后从b数组向前遍历,找到b[i]==anss的之后再找b[i]==anss-1的....以此类推
#include <bits/stdc++.h> using namespace std; int anss=0,i,n,j,a[10005],dp[10005],temp,b[10005],c[10005]; int main() { cin>>n; for(i=0;i<n;i++) { cin>>a[i]; dp[i]=0x7ffffff; } for(i=0;i<n;i++) { temp=lower_bound(dp,dp+n,a[i])-dp; if(temp+1>anss) { anss++; } dp[temp]=a[i]; b[i]=temp; } cout<<anss<<endl; temp=anss-1; for(i=n-1;i>=0;i--) { if(b[i]==temp) { c[temp]=a[i]; temp--; } } for(i=0;i<anss;i++) { cout<<c[i]<<' '; } }