P1091 合唱队形(lis变式)

题目传送门

题意:
给你一个长度为n的序列,我们要找到一个1<=i<=n,使得在[1,i]序列严格上升,[i,n]序列严格下降,找到这个最长的长度,输出没在这个序列中的数的个数。

思路一:O(n^2 logn)
很容易想到枚举n个点,采用二分nlogn的方法在[1,i]求一遍lis,反过来从n跑到i求lis,然后看长度。值得注意的是,应该以a[i]为基准,大于a[i]的不能放到序列中。
比如这组数据:

9 1 2 3 4 1 4 3 2 1

如果不注意这点,我们在5号点,求得前面的最长上升子序列长度为4,后面也是4,但是他们拼起来不能成为合法序列。所以我们通过这种方法求lis的时候要屏蔽大于a[i]的数。

思路二:O(n^2)
也就是最朴素的求lis,时间复杂度为n^2,这时候这种方法就比二分的方法要好,因为这个题用二分的方法需要屏蔽比a[i]大的数,而朴素版本不用考虑这个问题。(因为二者原理不同,二分法的d[i]表示的是如果长度为i,最优的结尾元素,而朴素lis求法的f[i]表示以第i个元素结尾能够获得的最优长度。)

二分:

#include<bits/stdc++.h>
#define endl '\n'
#define null NULL
#define ls p<<1
#define rs p<<1|1
#define fi first
#define se second
#define mp make_pair
#define pb push_back
#define ll long long
#define int long long
#define vi vector<int>
#define mii map<int,int>
#define pii pair<int,int>
#define ull unsigned long long
#define all(x) x.begin(),x.end()
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define ct cerr<<"Time elapsed:"<<1.0*clock()/CLOCKS_PER_SEC<<"s.\n";
char *fs,*ft,buf[1<<20];
#define gc() (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<20,stdin),fs==ft))?0:*fs++;
inline int read(){int x=0,f=1;char ch=gc();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=gc();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=gc();}
return x*f;}
using namespace std;
const int N=1e2+5;
const int inf=0x7fffffff;
const int mod=998244353;
const double eps=1e-6;
const double PI=acos(-1);
int a[N],d[N],n;
int up(int p)
{
    memset(d,0,sizeof d);
    int len=1;d[1]=a[1];
    for(int i=1;i<p;i++)
    {
        if(a[i]>=a[p])
            continue;
        if(a[i]>d[len])
        {
            d[++len]=a[i];
        }
        else
        {
            int pos=lower_bound(d+1,d+len+1,a[i])-d;
            d[pos]=a[i];
        }
    }
    if(p==1)
        len=0;
    return len;
}
int down(int p)
{
    memset(d,0,sizeof d);
    int len=1;d[1]=a[p+1];
    for(int i=p+1;i<=n;i++)
    {
        if(a[i]>=a[p])
            continue;
        if(a[i]<d[len])
            d[++len]=a[i];
        else
        {
            int pos=lower_bound(d+1,d+len+1,a[i],greater<int>())-d;
            d[pos]=a[i];
        }
    }
    if(p==n)
        len=0;
    return len;
}
signed main()
{
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
    }
    int res=0;
    for(int i=1;i<=n;i++)
    {
        int len1=up(i);
        if(d[len1]==a[i])
            len1--;
        int len2=down(i);
        if(d[1]==a[i])
            len2--;
        res=max(res,len1+len2+1);
    }
    cout<<n-res<<endl;
}

朴素求法

#include<bits/stdc++.h>
#define endl '\n'
#define null NULL
#define ls p<<1
#define rs p<<1|1
#define fi first
#define se second
#define mp make_pair
#define pb push_back
#define ll long long
#define int long long
#define vi vector<int>
#define mii map<int,int>
#define pii pair<int,int>
#define ull unsigned long long
#define all(x) x.begin(),x.end()
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define ct cerr<<"Time elapsed:"<<1.0*clock()/CLOCKS_PER_SEC<<"s.\n";
char *fs,*ft,buf[1<<20];
#define gc() (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<20,stdin),fs==ft))?0:*fs++;
inline int read(){int x=0,f=1;char ch=gc();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=gc();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=gc();}
return x*f;}
using namespace std;
const int N=1e2+5;
const int inf=0x7fffffff;
const int mod=998244353;
const double eps=1e-6;
const double PI=acos(-1);
int a[N],dp[N][2],n;
signed main()
{
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
    }
    for(int i=1;i<=n;i++)
    {
        dp[i][0]=1;
        for(int j=1;j<i;j++)
        {
            if(a[i]>a[j])
                dp[i][0]=max(dp[i][0],dp[j][0]+1);
        }
    }
    for(int i=n;i>=1;i--)
    {
        dp[i][1]=1;
        for(int j=n;j>i;j--)
        {
            if(a[i]>a[j])
                dp[i][1]=max(dp[i][1],dp[j][1]+1);
        }
    }
    int res=0;
    for(int i=1;i<=n;i++)
    {
        res=max(res,dp[i][0]+dp[i][1]-1);
    }
    cout<<n-res<<endl;
}

猜你喜欢

转载自blog.csdn.net/Joker_He/article/details/106249410