zoukankan      html  css  js  c++  java
  • 二分求LIS并打印结果

    1275: God's ladder [DP]

    时间限制: 1 Sec 内存限制: 128 MB  Special Judge

    题目描述

    天明来到神之宫殿,在他眼前出现了若干个石柱,每个石柱上有1枚金币,天明可以任意选择一个石柱开始,然后向前方的石柱瞬移,而且他所瞬移到的石柱的高度必须要大于现在所在石柱的高度。

    求天明所能获得的最大金币数以及任意一种可以获得这么多金币的路线(每个石柱的高度)。

    输入

    第一行一个数n,表示石柱的个数。

    然后2~n+1行,每行一个石柱的高度h[i],分别是1,2,,n石柱的高度。

    2<n<200100;1<= h[i] < 15000000;

    输出

    第一行为一个数m,表示最大金币数。

    2~m+1行,每行一个数,分别是每次所瞬移到的石柱的高度。

    样例输入

    7

    3

    1

    2

    5

    9

    6

    7

    样例输出

    5

    1

    2

    5

    6

    7

    代码:
    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    #define  inf  0x3f3f3f3f
    int dp[200220],list[200220],a[200220];//dp[i]保存lis为i时最小的元素,list保存每个元素的lis;
    int main()
    {
        int n,i,j,k,p;
        while (cin>>n){p=0;
        memset(dp,inf,sizeof(dp));
            for(i=0;i<n;i++){
                scanf("%d",&a[i]);
        *lower_bound(dp,dp+n,a[i])=a[i];          //记录更新长度对应的最大潜力元素时,顺便记录下该元素对应的LIS长度
        list[p++]=lower_bound(dp,dp+n,a[i])-dp+1;
            }
            k=lower_bound(dp,dp+n,inf)-dp;     //总的LIS
            cout<<k<<endl;
            int m=k;
            for(i=n-1;i>=0;i--){                          //从右至左扫描一遍直至找全LIS长度
                if(k==list[i])
                dp[k--]=a[i];
                if(!k) break;
        }
        for(i=1;i<=m;i++)printf("%d ",dp[i]);
        }
        return 0;
    }
     
     
    更快的解法,减小二分搜索的次数:
    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    #define  inf  0x3f3f3f3f
    int dp[200220],list[200220],a[200220];
    int main()
    {
        int n,i,j,k,p;
        while (cin>>n){p=0;k=0;
        memset(dp,inf,sizeof(dp));
            for(i=0;i<n;i++){
                scanf("%d",&a[i]);
     if(a[i]>dp[k])  dp[++k]=a[i];      // 设置k为当前最长lis值
       else*lower_bound(dp,dp+k,a[i])=a[i];  
         list[p++]=lower_bound(dp,dp+k,a[i])-dp+1;
            }
        //    k=lower_bound(dp,dp+n,inf)-dp;
        k=k+1;
            printf("%d ",k);
            int m=k;
            for(i=n-1;i>=0;i--){
                if(k==list[i])
                dp[k--]=a[i];
                if(!k) break;
        }
        for(i=1;i<=m;i++)printf("%d ",dp[i]);
        }
        return 0;
    }
    从右至左扫描的必要性:
    由于是求单调上升子序列,所以最大值定在右侧。
    又具有相同lis的不同元素中,潜力最大的也在右侧。
    即假设一个元素的lis为n(n>2),则在这个元素之前定有lis为(n-1)且小于此元素的原宿存在。
  • 相关阅读:
    MVC思想-程序的控制流程-Struts2和SpringMVC黑马流程图
    代理模式
    显卡
    感悟:Java新手一点想法
    java企业级开发的实质就是前台后台如何交互的-各个对象之间如何交互,通信的-程序执行的流程是怎样的
    $.ajax()方法详解--极快瑞中的阿贾克斯函数
    初学者必读之AJAX简单实例2
    初学者必读原生AJAX-异步的javaScript和XML
    c#输入方法名来调用方法(反射)
    unity接入讯飞教程
  • 原文地址:https://www.cnblogs.com/zzqc/p/6541571.html
Copyright © 2011-2022 走看看