[P1020]导弹拦截 (贪心/DP/二分/单调队列)

一道很经典的题

这道题就是要求一个最长单调不升子序列和一个最长单调上升子序列。

先打了一个n2复杂度的

用DP

#include<bits/stdc++.h>
using namespace std;
#define N 10005
int f[N],a[N];
int n;
int cnt1,cnt2,tot;
int main()
{
    while(scanf("%d",&a[++n])!=EOF);
    n--;
//    cout<<n<<endl;
//    for(int i=1;i<=n;++i)cout<<a[i]<<' ';cout<<endl;
    for(int i=1;i<=n;i++)
    {
        f[i]=1;
        for(int j=1;j<i;j++)
            if(a[i]<=a[j]&&f[i]<f[j]+1) f[i]=f[j]+1;
        if(f[i]>cnt1) cnt1=f[i];
    }
    memset(f,0,sizeof(f));
    for(int i=1;i<=n;i++)
    {
        f[i]=1;
        for(int j=1;j<=i;j++)
            if(a[i]>a[j]&&f[i]<f[j]+1) f[i]=f[j]+1;
        if(cnt2<f[i]) cnt2=f[i];
    }
    printf("%d\n%d\n",cnt1,cnt2);
    return 0;
}

然后可以根据单调性进行优化

但是思想就不一样了

用二分简化成nlogn的

#include<bits/stdc++.h>
using namespace std;
int a[100005],f[100005],l[100005];
struct cmp{bool operator()(int a,int b){return a>b;}};
int main()
{
    int n=1;
    while(cin>>a[n])n++;
    n--;
    int con=1,cont=1;
    l[1]=f[1]=a[1];
    for(int i=2;i<=n;i++)
    {
        if(l[cont]>=a[i])l[++cont]=a[i];
        else l[upper_bound(l+1,l+cont+1,a[i],cmp())-l]=a[i];
        if(f[con]<a[i])f[++con]=a[i];
        else f[lower_bound(f+1,f+con+1,a[i])-f]=a[i];
    }
    cout<<cont<<" "<<con;
    return 0;
}

还有一个大佬的树状数组的

我放在下面

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int f[1000000];
int z[1000000];
int lowbit(int x)
{
    return x&-x;
}
int big;
inline int ask(int x)//这是用来求单调上升子序列的
{
    int r=0;
    for(int i=x;i>0;i-=lowbit(i))
        r=max(r,f[i]);
    return r;
}
inline void add(int x,int v)//这也是用来求单调上升子序列的
{
    for(int i=x;i<=big;i+=lowbit(i))
        f[i]=max(f[i],v);
}
inline int que(int x)//这是用来求最长单调不升子序列的
{
    int r=0;
    for(int i=x;i<=big;i+=lowbit(i))
        r=max(r,f[i]);
    return r;
}
inline void psh(int x,int v)//这也是用来求最长单调不升子序列的
{
    for(int i=x;i>0;i-=lowbit(i))
        f[i]=max(f[i],v);
}
int tot;
int a[1000000];
int ans;
int main()
{
    tot=1;
    while(scanf("%d",&a[tot])!=EOF)
    {
        big=max(big,a[tot]);
        z[tot]=a[tot];
        tot++;
    }
    tot--;//读入并统计个数
    for(int i=1;i<=tot;i++)//求最长单升子序列,树状数组中保存的是0~a[i]的最大值
    {
        int x=ask(a[i])+1;
        ans=max(ans,x);
        add(a[i]+1,x);//因为是严格单升所以这里要+1
    }
    memset(f,0,sizeof(f));//清空树状数组,用来求下面的不降子序列
    int num=0;
    for(int i=1;i<=tot;i++)//求最长不降子序列,树状数组里存的是a[i]~inf的最大值
    {
        int x=que(a[i])+1;
        num=max(num,x);
        psh(a[i],x);//因为是不升而不是严格单降所以不用-1或+1
    }
    printf("%d\n%d",num,ans);
    return 0;
}
树状数组

猜你喜欢

转载自www.cnblogs.com/fsy2017/p/9933042.html