• 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;
    }
  • 相关阅读:
    五种常见的 PHP 设计模式
    转载:php header下载乱码 空格 问题
    PHP程序员最常犯的11个MySQL错误
    启迪人心:10个的有关编程的至理名言
    如何使用搜索技巧来成为一名高效的程序员
    随机验证码
    产生sql表中表示字段, 实现自增列
    在当前页面弹出对话框
    读取页面传入的URL值
    Sql临时表
  • 原文地址:https://www.cnblogs.com/shimu/p/5721108.html
Copyright © 2020-2023  润新知