【日记】12.17

12.17

洛谷突然黄名?

DP

1.洛谷P1020:最长不上升子序列(LNIS)和最小不上升子序列个数。

首先是LNIS:

朴素\(O(n^2)\)做法:从后往前,dp[i]=max(dp[i],dp[j]+1),满足i<j&&a[i]>=a[j]。

快速\(O(n\log n)\)做法:从前往后,每次找到序列中第一个比它小的数,更新优化,具体原理可见:https://blog.csdn.net/wqtltm/article/details/81253935,看图相当不错。同样发现这些数组可以进行复用,因此只需要一个d数组即可,空间复杂度$O(n)$

其次是最小划分出序列的个数。

有一个Dilworth定理,简单来讲就是,最小不上升子序列个数=最长上升子序列长度(把不去掉,把个数换成长度)。其实感性是可以理解的。

那么再跑一个LIS即可。

注意:找第一个比他小的数,可以用upper_bound(a+1,a+n+1,x,greater<int>())来做,不要用lower_bound()+1,容易出锅。小于等于同理。

#include<bits/stdc++.h>
using namespace std;
const int M=1e5+20;
int cnt,a[M];
struct LNIS{
    int dp[M],n;
    void init(){n=cnt;}
    void run(){
        int ans=0;
        for(int i=n;i>=1;--i){
            dp[i]=1;
            for(int j=n;j>=i+1;--j)
                if (a[i]>=a[j])
                    dp[i]=max(dp[i],dp[j]+1);
            ans=max(ans,dp[i]);
        }
        printf("%d\n",ans);
    }
}L1;
struct LIS{
    int dp[M],n;
    void init(){n=cnt;}
    void run(){
        int ans=0;
        for(int i=1;i<=n;++i){
            dp[i]=1;
            for(int j=1;j<=i-1;++j)
                if (a[i]>a[j])
                    dp[i]=max(dp[i],dp[j]+1);
            ans=max(ans,dp[i]);
        }
        printf("%d\n",ans);
    }
}L2;
int main(){
    int c;
    while(~scanf("%d",&c))
        a[++cnt]=c;
    L1.init();
    L2.init();
    L1.run();
    L2.run();
    return 0;
}

nlogn做法:

#include<bits/stdc++.h>
using namespace std;
const int M=1e5+20;
int cnt,a[M];
struct LNIS{
    int dp[M],d[M],n;
    void init(){n=cnt;}
    void run(){
        int len=1;
        for(int i=1;i<=n;++i){
            int p=upper_bound(d+1,d+len+1,a[i],greater<int>())-d;//找到第一个小于a[i]的下标
            dp[i]=p,d[p]=a[i];
            if (p>len)
                len=p;
        }
        int ans=0;
        for(int i=1;i<=n;++i)
            ans=max(ans,dp[i]);
        printf("%d\n",ans);
    }
}L1;
struct LIS{
    int dp[M],d[M],n;
    void init(){n=cnt;}
    void run(){
        int len=0;
        for(int i=1;i<=n;++i){
            int p=lower_bound(d+1,d+len+1,a[i])-d;//找到第一个大于等于a[i]的下标
            dp[i]=p,d[p]=a[i];
            if (p>len)
                len=p;
        }
        int ans=0;
        for(int i=1;i<=n;++i)
            ans=max(ans,dp[i]);
        printf("%d\n",ans);
    }
}L2;
int main(){
    int c;
    while(~scanf("%d",&c))
        a[++cnt]=c;
    L1.init();
    L2.init();
    L1.run();
    L2.run();
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/diorvh/p/12078800.html