zoukankan      html  css  js  c++  java
  • 最长单调递增子序列 POJ 3903 Stock Exchange .

    题目传送门 : -------->点这里点这里<----------

    题目大意:

    给出一串正整数,个数为N个。1<=N<=100000。求最长单调递增子序列长度。

    样例输入:

    6 
    5 2 1 4 5 3 
    3  
    1 1 1 
    4 
    4 3 2 1

     样例输出:

    3 
    1 
    1

     ===================================================================

    最长单调递增子序列问题的DP朴素算法复杂度为 O(n^2);

    然而题目中的 N 最大有10^5 一定是超时,因此可以采用nlogn优化。

    优化思路一:      DP + 二分查找

             #define MAX 100010

       A[MAX];//记录数字的数组

       DP[MAX];///记录A[i] 的DP 值。

       re[MAX];//记录到 第 i 个数字时,最大的子序列长度为 re[i];

       set[MAX];//记录DP值为 i 的最小的数 为 set[i];

      算法过程与 朴素 的DP算法一致,只是记录了步长一定时最小的数,并使用了二分查找,找出下一个元素的步长。

      更新 re[i];    set[DP[i]] = min(set[DP[i],A[i]);

      =================================================================================

    优化思路二:      贪心 + 二分查找

        非常神奇的一个事情,这题可以不使用DP解决。使用贪心算法。

       贪心策略:

    1. 初始化一个数组 d[MAX] 初始化为 0 , 变量 len = 0;
    2. 读入一个数字  num 。
    3. 如果num > d[len] , d[len++] = num; 继续 2 。
    4. 如果num >= d[len] , 使用二分法查看d数组,0 - len 的区域,找出不比num小的最小的数使用num替换他。继续 2 。
    5. 输出 len . 为 最长子序列长度。

       ======================================================

      其实这个算法与思路一是一样的。思路一种可以发现,其实re数组的大部分空间是浪费的,只要记录当前最长的子序列长度即可。而后的set数组记录的是步长为k时最小的数字为set[k];

    事实上可以将d数组将这两个数组的功能合并了,首先d数组中的每一个  d[i] 都是步长为 i + 1 时 的 最小的元素。 其次,长度len又是最长子序列的长度。

    为什么我们后面的小的元素可以往前插入呢?

        我们最后需要输出的是 子序列的长度 len 而不是子序列的本身,所以往前替换元素不会影响 len 的值。 而如果往前替换元素时发现,新的子序列比原来记录的旧的子序列长,那么也就自动覆盖了原来的序列,当前的len还是最长子序列长。

    代码:

     1 #include <iostream>
     2 #include <stdio.h>
     3 #include <string.h>
     4 #define MAX 100010
     5 
     6 using namespace std;
     7 int A[MAX];
     8 int d[MAX];
     9 ////////////////////////////
    10 void init()
    11 {
    12     memset(d,0,MAX * sizeof(int));
    13     return ;
    14 }
    15 
    16 int main()
    17 {
    18     int N;
    19     while(~scanf("%d",&N))
    20     {
    21         init();
    22         for(int i = 0 ; i < N ; i++) scanf("%d",&A[i]);
    23         int len = 0;
    24         for(int i = 0 ; i < N ; i++)
    25         {
    26             if(d[len] < A[i])
    27             {
    28                 d[++len] = A[i];
    29             }
    30             else if (d[len] == A[i]) continue;
    31             else
    32             {
    33                 int l = 0;
    34                 int r = len;
    35                 int ans = 0;
    36                 int key;
    37                 while(l <= r)
    38                 {
    39                     key = (l + r) / 2;
    40                     if(d[key] == A[i])
    41                     {
    42                         ans = key;
    43                         break;
    44                     }
    45                     if(d[key] > A[i])
    46                     {
    47                         ans = key;
    48                         r = key - 1;
    49                     }
    50                     else
    51                     {
    52                         l = key + 1;
    53                     }
    54                 }
    55                 d[ans] = A[i];
    56             }
    57         }
    58         cout << len << endl;
    59 
    60     }
    61     return 0;
    62 }
  • 相关阅读:
    Android 进阶 Fragment 介绍和使用 (一)
    Android Activity 详述
    android 自定义组合控件 顶部导航栏
    android 开发进阶自定义控件 类似 TextView
    android 开发进阶 自定义控件-仿ios自动清除控件
    Android 在程序中动态添加 View 布局或控件
    Android ImageView 详述
    关于北京工作居住证相关问题
    Android中常用的5大布局详述
    关于北京计算机技术与软件专业技术资格(水平)考试(软考)报名及证书的领取
  • 原文地址:https://www.cnblogs.com/ticsmtc/p/4982503.html
Copyright © 2011-2022 走看看