zoukankan      html  css  js  c++  java
  • LIS

    LIS(最长上升子序列)(含等于的是最长不上升子序列)

    vj atcoder 上的板子题

    首先介绍下(O(n^2))的dp

    首先单独一个数我们直接把他看成就是一个子序列,这个子序列的LIS就是1.我们用一个DP[i]记录从之前到i的LIS,不难得到状态转移方程dp[i]=max(dp[i],dp[j]+1) ( j=[1,i-1])

    试想一下,1 3 2 5 1 4 每次跑i就找之前的子序列有没有那个子序列的最大值比a[i]###还小的,有的话直接把a[i]加入,如果没有的话就把他单独弄成一个新的子序列 dp[i]=1

    dp[1]=1 子序列1

    dp[2]=2 子序列1 3

    dp[3]=2 子序列1 3 和子序列1 2

    dp[4]=3 子序列1 2 5和1 3 5

    dp[5]=1 子序列就是1,之前没有找到比他小的就单独创建

    #include <bits/stdc++.h>
    using namespace std;
    const int maxn=4e5+7;
    #define  ll long long
    int  a[maxn];
    int  dp[maxn];
    int n;
    int main()
    {
        scanf("%d",&n);
        for (int i=1;i<=n;++i)
        {
            scanf("%d",a+i);
        }
        int ans=0;
        memset(dp,0,sizeof (dp));
        dp[1]=1;///初始化
        for (int i=1;i<=n;++i)
        {
            for (int j=1;j<i;++j)///找寻之前的是否可以合并
            {
                if (a[i]>a[j])
                {
                    ans = max(ans,dp[i]=max(dp[i],dp[j]+1));
                }
            }
            ans=max(ans,dp[i]=max(dp[i],1));///判断是否加入原来的子序列,否则直接以a[i]单独创建一个子序列
        }
        printf("%d
    ",ans);
        return 0;
    }
    

    其实O(n^2)的复杂度属实大,很容易被卡,所以接下来带来一种树状数组的解法(如果不会树状数组或者不会维护区间最值的请点此

    其实dp[i]=max(dp[i],dp[j]+1) ( j=[1,i-1])这个状态转移方程每次都要往之前查找一遍

    不妨直接用树状数组储存LIS数据,然后按照输入数据从大到小进行排列

    query查询1到当前位置之间的最大lis,向后直接更新包括当前位置的lis,由于数据直接是从小到大,不影响之后比他大的数更新lis

    以输入值为第一权值,以排序位置作为第二权值排序

    #include <bits/stdc++.h>
    using namespace std;
    const int maxn=4e5+7;
    #define  ll long long
    struct Node{
    int xx,yy;
     bool operator <(const Node&rhs)const {
         return xx==rhs.xx?yy<rhs.yy:xx<rhs.xx;
       }
    }a[maxn];
    int c[maxn]={0};
    const int INF=0x3f3f3f3f;
    inline int lowbit(int x)
    {
        return x&(-x);
    }
    int query(int x)///求区间1-x之间的最大值
    {
        int res=-INF;
        for(;x;x-=x&(-x)) res=max(res,c[x]);///由于是求1-x所以一定满足y-lowbit(y)>x无需像之前一样特判
        return res;
    
    }
    void modify(int idx,int math,int k)///修改 k是边界 idx修改地址 math修改值
    {
    
        for(;idx<=k;idx+=idx&(-idx)) c[idx]=max(c[idx],math);///修改的时候只是遍历所有包含位置idx的值
    }
    ///这两种都可以过
    void modify(int idx,int math,int k)///修改 k是边界 idx修改地址 math修改值
    {
       c[idx]=math;
        while (idx<=k)
        {
            ///该区域肯定要包括自己,所以首先把自己加入进去
            for (int i=1;i<lowbit(idx);i<<=1)///直接枚举
            {
                    c[idx]=max(c[idx],c[idx-i]);
            }
            idx+=lowbit(idx);
        }
    }
    
    int main()
    {
        memset(a,0,sizeof(a));
        memset(c,0,sizeof(c));
        int n;
        scanf("%d",&n);
        for (int i=1;i<=n;++i)
        {
            cin>>a[i].xx;
            a[i].yy=i;
        }
        sort(a+1,a+1+n);
        int ans=0;
        for (int i=1;i<=n;++i)
        {
            int maxx=query(a[i].yy);///找到a[i]所在编号之前最大的lis
            modify(a[i].yy,++maxx,n);///更新在此之前的编号的lis
            ans=max(ans,maxx);
        }
        printf("%d
    ",ans);
        return 0;
    }
    ##懒得去重了
    
    齐芒行,川锋明!
  • 相关阅读:
    查看mysql版本的四种方法及常用命令
    newInstance和new的区别(good)
    Citrix 服务器虚拟化之六 Xenserver虚拟机创建与快照
    Java实现 蓝桥杯 算法训练 排序
    Java实现 蓝桥杯 算法训练 排序
    Java实现 蓝桥杯 算法训练 排序
    Java实现 蓝桥杯 算法训练 2的次幂表示
    Java实现 蓝桥杯 算法训练 2的次幂表示
    Java实现 蓝桥杯 算法训练 前缀表达式
    Java实现 蓝桥杯 算法训练 前缀表达式
  • 原文地址:https://www.cnblogs.com/qimang-311/p/13380082.html
Copyright © 2011-2022 走看看