• 最长上升子序列模型


    1017. 怪盗基德的滑翔翼

    怪盗基德是一个充满传奇色彩的怪盗,专门以珠宝为目标的超级盗窃犯。
    而他最为突出的地方,就是他每次都能逃脱中村警部的重重围堵,而这也很大程度上是多亏了他随身携带的便于操作的滑翔翼。
    有一天,怪盗基德像往常一样偷走了一颗珍贵的钻石,不料却被柯南小朋友识破了伪装,而他的滑翔翼的动力装置也被柯南踢出的足球破坏了。
    不得已,怪盗基德只能操作受损的滑翔翼逃脱。
    假设城市中一共有N幢建筑排成一条线,每幢建筑的高度各不相同。
    初始时,怪盗基德可以在任何一幢建筑的顶端。
    他可以选择一个方向逃跑,但是不能中途改变方向(因为中森警部会在后面追击)。
    因为滑翔翼动力装置受损,他只能往下滑行(即:只能从较高的建筑滑翔到较低的建筑)。
    他希望尽可能多地经过不同建筑的顶部,这样可以减缓下降时的冲击力,减少受伤的可能性。
    请问,他最多可以经过多少幢不同建筑的顶部(包含初始时的建筑)?
    输入格式
    输入数据第一行是一个整数K,代表有K组测试数据。
    每组测试数据包含两行:第一行是一个整数N,代表有N幢建筑。第二行包含N个不同的整数,每一个对应一幢建筑的高度h,按照建筑的排列顺序给出。
    输出格式
    对于每一组测试数据,输出一行,包含一个整数,代表怪盗基德最多可以经过的建筑数量。
    数据范围
    1≤K≤100,
    1≤N≤100,
    0<h<10000
    输入样例:
    3
    8
    300 207 155 299 298 170 158 65
    8
    65 158 170 298 299 155 207 300
    10
    2 1 3 4 5 6 7 8 9 10
    输出样例:
    6
    6
    9

    思路: 左右各求一次最长下降子序列

    #include<bits/stdc++.h>
    using namespace std;
    const int N=110;
    int a[N],dp[N];
    int main(){
        int T;
        cin>>T;
        while(T--){
            int n;
            cin>>n;
            for(int i=1;i<=n;++i)
                cin>>a[i];
            int m=0;
            for(int i=1;i<=n;++i){
                int p=lower_bound(dp+1,dp+m+1,a[i],greater<int>())-dp;
                dp[p]=a[i];
                m=max(p,m);
            }
            int ans=m;
            reverse(a+1,a+1+n);
            m=0;
            for(int i=1;i<=n;++i){
                int p=lower_bound(dp+1,dp+m+1,a[i],greater<int>())-dp;
                dp[p]=a[i];
                m=max(p,m);
            }
            ans=max(ans,m);
            cout<<ans<<endl;
        }
        return 0;
    }
    

    1014. 登山

    五一到了,ACM队组织大家去登山观光,队员们发现山上一个有N个景点,并且决定按照顺序来浏览这些景点,即每次所浏览景点的编号都要大于前一个浏览景点的编号。
    同时队员们还有另一个登山习惯,就是不连续浏览海拔相同的两个景点,并且一旦开始下山,就不再向上走了。
    队员们希望在满足上面条件的同时,尽可能多的浏览景点,你能帮他们找出最多可能浏览的景点数么?
    输入格式
    第一行包含整数N,表示景点数量。
    第二行包含N个整数,表示每个景点的海拔。
    输出格式
    输出一个整数,表示最多能浏览的景点数。
    数据范围
    2≤N≤1000
    输入样例:
    8
    186 186 150 200 160 130 197 220
    输出样例:
    4

    思路: 枚举每个点为登到的最高点,算出以每个点开始从左向右最长的上升子序列和从右向左的最长上升子序列之和。答案就是最大值。

    #include<bits/stdc++.h>
    using namespace std;
    const int N=1010;
    int a[N],dp[N],l[N],r[N];
    int main(){
        int n;
        cin>>n;
        for(int i=1;i<=n;++i)
            cin>>a[i];
        int m=0;
        for(int i=1;i<=n;++i){
            int p=lower_bound(dp+1,dp+1+m,a[i])-dp;
            dp[p]=a[i];
            m=max(m,p);
            l[i]=p;
        }
        reverse(a+1,a+1+n);
        m=0;
        for(int i=1;i<=n;++i){
            int p=lower_bound(dp+1,dp+m+1,a[i])-dp;
            dp[p]=a[i];
            m=max(m,p);
            r[i]=p;
        }
        int ans=0;
        for(int i=1;i<=n;++i){
            ans=max(ans,l[i]+r[n-i+1]-1);
        }
        cout<<ans<<endl;
        return 0;
    }
    

    1012. 友好城市

    Palmia国有一条横贯东西的大河,河有笔直的南北两岸,岸上各有位置各不相同的N个城市。
    北岸的每个城市有且仅有一个友好城市在南岸,而且不同城市的友好城市不相同。
    每对友好城市都向政府申请在河上开辟一条直线航道连接两个城市,但是由于河上雾太大,政府决定避免任意两条航道交叉,以避免事故。
    编程帮助政府做出一些批准和拒绝申请的决定,使得在保证任意两条航线不相交的情况下,被批准的申请尽量多。
    输入格式
    第1行,一个整数N,表示城市数。
    第2行到第n+1行,每行两个整数,中间用1个空格隔开,分别表示南岸和北岸的一对友好城市的坐标。
    输出格式
    仅一行,输出一个整数,表示政府所能批准的最多申请数。
    数据范围
    1≤N≤5000,
    0≤xi≤10000
    输入样例:
    7
    22 4
    2 6
    10 3
    15 12
    9 8
    17 17
    4 2
    输出样例:
    4

    思路: 画图可以发现以某一排为基准,i和n,j和m建道 i<j,n>m会有交叉。对申请以第一排个序,满足条件的最大个数就是最长非降序子序列的个数。

    #include<bits/stdc++.h>
    using namespace std;
    typedef pair<int,int> PII;
    PII a[5010];
    int f[5010];
    int main(){
        int n;
        cin>>n;
        for(int i=1;i<=n;++i)
            cin>>a[i].first>>a[i].second;
        sort(a+1,a+1+n);
        int m=0;
        for(int i=1;i<=n;++i){
            int x=a[i].second;
            int p=upper_bound(f+1,f+1+m,x)-f;
            f[p]=x;
            m=max(p,m);
        }
        cout<<m<<endl;
        return 0;
    }
    

    1010. 拦截导弹

    某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统。
    但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。
    某天,雷达捕捉到敌国的导弹来袭。
    由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹。
    输入导弹依次飞来的高度(雷达给出的高度数据是不大于30000的正整数,导弹数不超过1000),计算这套系统最多能拦截多少导弹,如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。
    输入格式
    共一行,输入导弹依次飞来的高度。
    输出格式
    第一行包含一个整数,表示最多能拦截的导弹数。
    第二行包含一个整数,表示要拦截所有导弹最少要配备的系统数。
    输入样例:
    389 207 155 300 299 170 158 65
    输出样例:
    6
    2

    思路:
    这道题几乎无处不在。
    说一下结论:最少上升/下降子序列的个数对应最长非上升/非下降子序列的个数,最少非降序/非上升子序列的个数对应最长下降/上升子序列的个数。

    #include<bits/stdc++.h>
    using namespace std;
    const int N=1010;
    int dp[N],a[N];
    int main(){
        int cnt=0;
        while(cin>>a[cnt++]){}
        int ans1,ans2,m=0;
        for(int i=0;i<cnt-1;++i){
            int p=upper_bound(dp+1,dp+1+m,a[i],greater<int>())-dp;
            dp[p]=a[i];
            m=max(p,m);
        }
        ans1=m;
        m=0;
        for(int i=0;i<cnt-1;++i){
            int p=lower_bound(dp+1,dp+1+m,a[i])-dp;
            dp[p]=a[i];
            m=max(p,m);
        }
        ans2=m;
        cout<<ans1<<endl<<ans2<<endl;
        return 0;
    }
    

    187. 导弹防御系统

    为了对抗附近恶意国家的威胁,R国更新了他们的导弹防御系统。
    一套防御系统的导弹拦截高度要么一直 严格单调 上升要么一直 严格单调 下降。
    例如,一套系统先后拦截了高度为3和高度为4的两发导弹,那么接下来该系统就只能拦截高度大于4的导弹。
    给定即将袭来的一系列导弹的高度,请你求出至少需要多少套防御系统,就可以将它们全部击落。
    输入格式
    输入包含多组测试用例。
    对于每个测试用例,第一行包含整数n,表示来袭导弹数量。
    第二行包含n个不同的整数,表示每个导弹的高度。
    当输入测试用例n=0时,表示输入终止,且该用例无需处理。
    输出格式
    对于每个测试用例,输出一个占据一行的整数,表示所需的防御系统数量。
    数据范围
    1≤n≤50
    输入样例:
    5
    3 5 2 4 1
    0
    输出样例:
    2
    样例解释
    对于给出样例,最少需要两套防御系统。
    一套击落高度为3,4的导弹,另一套击落高度为5,2,1的导弹。

    思路: 枚举每个点属于上升序列还是下降序列,迭代加深优化。

    #include<bits/stdc++.h>
    using namespace std;
    const int N=60;
    int n,a[N],up[N],down[N];
    int Max;
    bool dfs(int u,int l,int r){
        if(u==n+1) return true;
        if(l+r>Max) return false;
        bool in=false;
        //最长上升子序列的个数->最长非上升子序列的长度 upper_bound(,,,greater);
        int p=upper_bound(up+1,up+1+l,a[u],greater<int>())-up;
        int t=up[p];
        up[p]=a[u];
        if(dfs(u+1,max(p,l),r)) return true;
        up[p]=t;
        //最长下降子序列的个数->最长非下降子序列的长度 upper_bound(,,)
        p=r+1;
        int ll=1,rr=r;
        while(ll<=rr){
            int m=(ll+rr)/2;
            if(a[u]>=down[m]) ll=m+1;
            else {
                p=m;
                rr=m-1;
            }
        }
        t=down[p];
        down[p]=a[u];
        if(dfs(u+1,l,max(r,p))) return true;
        down[p]=t;
        return false;
    }
    int main(){
        while(cin>>n,n){
            for(int i=0;i<n;++i)
                cin>>a[i];
            for(int i=1;i<=n;++i){
                Max=i;
                if(dfs(1,0,0)){
                    cout<<i<<endl;
                    break;
                }
            }
        }
        return 0;
    }
    

    272. 最长公共上升子序列

    熊大妈的奶牛在小沐沐的熏陶下开始研究信息题目。
    小沐沐先让奶牛研究了最长上升子序列,再让他们研究了最长公共子序列,现在又让他们研究最长公共上升子序列了。
    小沐沐说,对于两个数列A和B,如果它们都包含一段位置不一定连续的数,且数值是严格递增的,那么称这一段数是两个数列的公共上升子序列,而所有的公共上升子序列中最长的就是最长公共上升子序列了。
    奶牛半懂不懂,小沐沐要你来告诉奶牛什么是最长公共上升子序列。
    不过,只要告诉奶牛它的长度就可以了。
    数列A和B的长度均不超过3000。
    输入格式
    第一行包含一个整数N,表示数列A,B的长度。
    第二行包含N个整数,表示数列A。
    第三行包含N个整数,表示数列B。
    输出格式
    输出一个整数,表示最长公共子序列的长度。
    数据范围
    1≤N≤3000,序列中的数字均不超过231−1
    输入样例:
    4
    2 2 1 3
    2 1 2 3
    输出样例:
    2

    闫氏dp分析法:最长上升子序列和最长公共子序列两种思想结合

    #include<bits/stdc++.h>
    using namespace std;
    const int N=3010;
    int f[N][N],a[N],b[N];
    int main(){
        int n;
        cin>>n;
        for(int i=1;i<=n;++i) cin>>a[i];
        for(int i=1;i<=n;++i) cin>>b[i];
        int ans=0;
        for(int i=1;i<=n;++i){
            int maxv=0;
            for(int j=1;j<=n;++j){
                f[i][j]=f[i-1][j];
                if(a[i]==b[j]){
                    f[i][j]=max(f[i][j],1);
                    f[i][j]=max(f[i][j],maxv+1);
                }
                if(b[j]<a[i])
                    maxv=max(f[i][j],maxv);
                ans=max(f[i][j],ans);
            }
        }
        cout<<ans<<endl;
        return 0;
    }
    

    1016. 最大上升子序列和

    一个数的序列 bi,当 b1<b2<…<bS 的时候,我们称这个序列是上升的。
    对于给定的一个序列(a1,a2,…,aN),我们可以得到一些上升的子序列(ai1,ai2,…,aiK),这里1≤i1<i2<…<iK≤N。
    比如,对于序列(1,7,3,5,9,4,8),有它的一些上升子序列,如(1,7),(3,4,8)等等。
    这些子序列中和最大为18,为子序列(1,3,5,9)的和。
    你的任务,就是对于给定的序列,求出最大上升子序列和。
    注意,最长的上升子序列的和不一定是最大的,比如序列(100,1,2,3)的最大上升子序列和为100,而最长上升子序列为(1,2,3)。
    输入格式
    输入的第一行是序列的长度N。
    第二行给出序列中的N个整数,这些整数的取值范围都在0到10000(可能重复)。
    输出格式
    输出一个整数,表示最大上升子序列和。
    数据范围
    1≤N≤1000
    输入样例:
    7
    1 7 3 5 9 4 8
    输出样例:
    18

    思路: O(n^2)暴力求法,dp记录的值不再是以该点结束最大长度而是以该点结束最大和。貌似贪心做不了,这道题权值比较大,下面一道按权展开的O(v*n)。

    #include<bits/stdc++.h>
    using namespace std;
    const int N=1010;
    int a[N],sum[N];
    int main(){
        int n;
        cin>>n;
        for(int i=1;i<=n;++i)
            cin>>a[i];
        int ans=0;
        for(int i=1;i<=n;++i){
            sum[i]=a[i];
            for(int j=1;j<i;++j){
                if(a[j]<a[i])
                sum[i]=max(sum[j]+a[i],sum[i]);
            }
            ans=max(ans,sum[i]);
        }
        cout<<ans;
        return 0;
    }
    

    Aggie’s Tasks

    Aggie is faced with a sequence of tasks, each of which with a difficulty value di and an expected profit pi. For each task, Aggie must decide whether or not to complete it. As Aggie doesn’t want to waste her time on easy tasks, once she takes a task with a difficulty di, she won’t take any task whose difficulty value is less than or equal to di.
    Now Aggie needs to know the largest profits that she may gain.
    Input
    The first line consists of one positive integer t (t ≤ 10), which denotes the number of test cases.
    For each test case, the first line consists one positive integer n (n ≤ 100000), which denotes the number of tasks. The second line consists of n positive integers, d1, d2, …, dn (di ≤ 100000), which is the difficulty value of each task. The third line consists of n positive integers, p1, p2, …, pn (pi ≤ 100), which is the profit that each task may bring to Aggie.
    Output
    For each test case, output a single number which is the largest profits that Aggie may gain.
    Sample Input
    1
    5
    3 4 5 1 2
    1 1 1 2 2
    Sample Output
    4

    思路: 这道题有两种权值,以求d上升序列v元素的最大和。 n^2太大,O(v*n)是1e7。将每个点按权值展开,长度就对成到了权值。例如

    #include<iostream>
    #include<cstdio>
    #include<queue>
    #include<cstring>
    #include<bits/stdc++.h>
    #define int long long
    using namespace std;
    const int N=100010;
    int a[N],b[N],dp[N];
    signed main(){
        int T;
        cin>>T;
        while(T--){
            int n;
            cin>>n;
            for(int i=1;i<=n;++i)
                cin>>a[i];
            for(int i=1;i<=n;++i)
                cin>>b[i];
            int m=0;
            for(int i=1;i<=n;++i){
                int p=lower_bound(dp,dp+m,a[i])-dp;
                for(int j=p;j<p+b[i];++j){
                    dp[j]=a[i];
                }
                m=max(m,p+b[i]);
            }
            cout<<m<<endl;
        }
        return 0;
    }
    
  • 相关阅读:
    第1课 Git、谁与争锋
    程序员最真实的10个瞬间
    程序员最真实的10个瞬间
    一文读懂前端缓存
    一文读懂前端缓存
    一文读懂前端缓存
    EF使用CodeFirst创建数据库和表
    EF使用CodeFirst创建数据库和表
    EF使用CodeFirst创建数据库和表
    ASP.NET MVC的过滤器笔记
  • 原文地址:https://www.cnblogs.com/jjl0229/p/12569186.html
Copyright © 2020-2023  润新知