hdu5773 最长上升子序列变形(附带模板)

先说说最长上升子序列的模板,时间复杂度O(nlogn)

最长上升子序列
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int N = 41000;
int a[N],p;       //a[i] 原始数据
int d[N];       //d[i] 长度为i的递增子序列的最小值

int BinSearch(int key, int low, int high)//二分查找,找不到则归0
{
    while(low<=high)
    {
        int mid = (low+high)>>1;
        if(key>d[mid] && key<=d[mid+1])
            return mid;
        else if(key>d[mid])
            low = mid+1;
        else
            high = mid-1;
    }
    return 0;
}

int LIS()
{
    int i,j;
    d[1] = a[1];
    int len = 1;        //递增子序列长度
    for(i = 2; i <= p; i++)
    {
        if(d[len]<a[i])
            j = ++len;
        else
            j = BinSearch(a[i],1,len) + 1;
        d[j] = a[i];
    }
    return len;
}

int main()
{
  //  freopen("in.txt","r",stdin);
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&p);
        for(int i = 1; i <= p; i++)
            scanf("%d",&a[i]);//输入的数组
        printf("%d
",LIS());
    }
    return 0;
}

dp[i]表示的是到目前为止,当子序列的长度为i时,对应数组a[]中的最小值,用len记录到目前为止可以组成的子序列的最长长度

当处理一个新的a[u]时,a[u]>dp[len]则直接dp[len+1]=a[u];

当a[u]<=dp[len]容易看出dp是一个严格单调递增的数组,则直接二分求解a[u]可以对dp的哪个元素进行更新

这题的题意是:

给你一堆乱序序列包含0,0可以变成任何整数包括负数,问最长上升子序列的长度

自己乱搞是在LIS模板的基础上碰到0就去更新dp数组每一个元素的值,若0在队首就将dp[1]=-1e9+7;

没想到居然过了大概数据比较水

#include <iostream>
#include <map>
#include <math.h>
#include <algorithm>
#include <vector>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <set>
#include<stdio.h>

using namespace std;
const int N=1e5+50;
const int M=1e9+7;
int t,n,a[N],dp[N];
int erfen(int l,int r,int u){
while(l<=r){
    int mid=(l+r)>>1;
    if(dp[mid]<a[u]&&dp[mid+1]>=a[u])
        return mid;
    else if(dp[mid]>a[u])
        r=mid-1;
    else
        l=mid+1;
}
    return 0;
}
int main()
{
   // freopen("in.txt","r",stdin);
    cin>>t;
    int cas=0;
    while(t--){
        cas++;
       scanf("%d",&n);
       for(int i=1;i<=n;i++)
            dp[i]=1000000+10;
        dp[0]=0;
        int i,j;
        for(i = 1;i <= n;i ++)
            scanf("%d",&a[i]);
        int len=0;
        dp[1]=min(dp[1],a[1]);
        len++;
       // cout<<dp[1]<<" "<<a[1]<<endl;
        for(i = 2;i <= n;i ++){
            //cout<<"r"<<endl;
            if(a[i]!=0){
                if(a[i]>dp[len])
                    j=++len;
                else
                    j=erfen(1,len,i)+1;
                dp[j]=a[i];
        }
            else{
                dp[len+1]=dp[len]+1;
                for(j=len;j>1;j--)
                    dp[j]=dp[j-1]+1;
                dp[1]=0;
                len++;
            }
    }
   printf("Case #%d: %d
",cas,len);
    }
    return 0;
}

题解是:

0可以转化成任意整数,包括负数,显然求LIS时尽量把0都放进去必定是正确的,因为0放不进去的原因无非就是非严格递增。为了保证严格递增,我们可以将每个权值S[i]减去i前面0的个数,再做LIS,就能保证结果是严格递增的。因此我们可以把0拿出来,对剩下的做O(nlogn)的LIS,统计结果的时候再算上0的数量。只能说是膜拜。当然有点小细节,自己注意一下

#include <iostream>
#include <map>
#include <math.h>
#include <algorithm>
#include <vector>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <set>
#include<stdio.h>

using namespace std;
const int N=1e5+50;
const int M=1e9+7;
int t,n,a[N],dp[N],num[N];
int erfen(int l,int r,int u){
while(l<=r){
    int mid=(l+r)>>1;
    if(dp[mid]<a[u]&&dp[mid+1]>=a[u])
        return mid;
    else if(dp[mid]<a[u])
        l=mid+1;
    else
        r=mid-1;
}
    return 0;
}
int main()
{
  //  freopen("in.txt","r",stdin);
    cin>>t;
    int cas=0;
    while(t--){
        int cnt0=0;
        cas++;
       scanf("%d",&n);
       for(int i=1;i<=n;i++)
            dp[i]=1000000+10,num[i]=0;
        dp[0]=0,num[0]=0;
        int i,j,cnt=0;
       for(i = 1;i <= n;i ++){
            scanf("%d",&a[i]);
            if(a[i]==0)
                cnt++,cnt0++;
            if(a[i]!=0)
            num[i]=cnt;
       }
        for(i=1,cnt=0;i<=n;i++){
            if(a[i]!=0){
            a[++cnt]=a[i]-num[i];
            //cout<<a[cnt]<<" "<<cnt<<endl;
            }
        //cout<<a[cnt]<<" "<<cnt<<endl;
        }

        int len=0;
        dp[1]=min(dp[1],a[1]);
        len++;
       if(cnt==0)
        len=0;
       // cout<<dp[1]<<" "<<a[1]<<endl;
        for(i = 2;i <= cnt;i ++){
                if(a[i]>dp[len])
                    j=++len;
                else
                    j=erfen(1,len,i)+1;
                dp[j]=a[i];
    //cout<<j<<" "<<dp[j]<<endl;
    }

   printf("Case #%d: %d
",cas,len+cnt0);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/shimu/p/5721108.html