1. 程式人生 > >[P1020]導彈攔截 (貪心/DP/二分/單調佇列)

[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;
}
樹狀陣列