求最大递减子列的三种方法
假设有一段数列为 8 2 3 4 1 3 2,求该数列的最长递减子列
一.动态规划
f ( i ) 为 以 a [ i ] 为 结 尾 的 最 大 递 减 子 列 长 度 当 i > j 时 如 果 ( a [ I ] < = a [ j ] ) f [ I ] = m a x ( f [ j ] + 1 , f [ i ] ) f(i)为以a[i]为结尾的最大递减子列\\长度当i>j时\\ 如果(a[I]<=a[j])\\ f[I]=max(f[j]+1,f[i]) f(i)为以a[i]为结尾的最大递减子列长度当i>j时如果(a[I]<=a[j])f[I]=max(f[j]+1,f[i])
#include <iostream>
using namespace std;
int main() {
int n;
int a[20];
int f[20];
int ans=-999999;
scanf("%d",&n);
for (int i=1; i<=n; i++) {
scanf("%d",&a[i]);
f[i]=1;
}
for (int i=1; i<=n; i++) {
for (int j=1; j<i; j++) {
if(a[j]>=a[i]) f[i]=max(f[i],f[j]+1);
}
}
for (int i=1; i<=n; i++) {
ans=max(ans,f[i]);
}
printf("%d",ans);
return 0;
}
二. 二分加贪心
以4 3 2 2 5 为例
4 | 3 | 2 | 2 | 8 |
---|---|---|---|---|
4 | ||||
4 | 3 | |||
4 | 3 | 2 | ||
4 | 3 | 2 | 2 | |
8 | 3 | 2 | 2 |
要用8替换4,这里是考虑一种可能性,因为可能在5
后还有数字,假设后面有7 6 6 5 4
那么这些会替换其他数字,获得更长
#include <iostream>
const int INF = -999999;
using namespace std;
int search(int *a,int r,int x)
{
int l=1;
int mid;
while (l<=r) {
mid=(l+r)/2;
if(a[mid]<=x) r=mid-1;
else l=mid+1;
}
return l;
}// 得到不小于x的a[i]的序数
int main() {
int n;
int a[20];
int low[20];
int ans=1;
scanf("%d",&n);
for (int i=1; i<=n; i++) {
scanf("%d",&a[i]);
low[i]=INF;
}
low[1]=a[1];
for (int i=2; i<=n; i++) {
if(a[i]<=low[ans]) low[++ans]=a[i];
else low[search(low,ans,a[i])]=a[i];
}
printf("%d",ans);
return 0;
}
三. 使用lower_bound( )函数
lower_bound( begin,end,num,cmp)查找第一个大于或等于num的地址
upper_bound( begin,end,num,cmp)查找第一个大于num的地址
他们被包含在
#include <algorithm>
可以用
#include <iostream>
#include <algorithm>
const int INF = -999999;
using namespace std;
int cmd(int x,int y)
{
return x>y;
}
int main() {
int n;
int a[20];
int low[20];
int ans=1;
scanf("%d",&n);
for (int i=1; i<=n; i++) {
scanf("%d",&a[i]);
low[i]=INF;
}
low[1]=a[1];
for (int i=2; i<=n; i++) {
if(a[i]<=low[ans]) low[++ans]=a[i];
else low[lower_bound(low,low+ans,a[i],cmd)-low]=a[i];
}
printf("%d",ans);
return 0;
}