最长单调递增子序列 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 }
时间: 2024-10-11 22:18:30

最长单调递增子序列 POJ 3903 Stock Exchange .的相关文章

{POJ}{3903}{Stock Exchange}{nlogn 最长上升子序列}

题意:求最长上升子序列,n=100000 思路:O(N^2)铁定超时啊....利用贪心的思想去找答案.利用栈,每次输入数据检查栈,二分查找替换掉最小比他大的数据,这样得到的栈就是更优的.这个题目确实不错,思路很好 #include <iostream> #include <string> #include <cstring> #include <cstdio> #include <algorithm> #include <memory>

poj 3903 Stock Exchange(最长上升子序列,模版题)

题目 #include<stdio.h> //最长上升子序列 nlogn //入口参数:数组名+数组长度,类型不限,结构体类型可以通过重载运算符实现 //数组下标从1号开始. int bsearch(int a[],int len,int num) { int left=1,right=len; while(left<=right) { int mid=(left+right)/2; if(num<=a[mid]) //若最长不下降子序列,改<= 为 < right=m

最长单调递增子序列的三种解法

问题描述: 找出由n个数组成的序列的最长单调递增子序列 解法一:转化成LCS问题求解,时间复杂度为O(n*n). 思路:原序列为A,把A按升序排序得到序列B,求出A,B序列的最长公共子序列,即为A的最长单调递增子序列. #include<iostream> #include<algorithm> #include<string> #include<cstdio> using namespace std; //转化成LCS问题,时间复杂度O(n*n) int

hdoj 5087 Revenge of LIS II 【第二长单调递增子序列】

题目:hdoj 5087 Revenge of LIS II 题意:很简单,给你一个序列,让你求第二长单调递增子序列. 分析:其实很简单,不知道比赛的时候为什么那么多了判掉了. 我们用O(n^2)的时间求单调递增子序列的时候,里面在加一层循环维护sum数组,表示前面有几个可以转移当当前,求前面sum的和保存到当前. 最后求最后一个sum[n-1]是否为1就ok,为1的话在最长的基础上减一,否则就是最长的. AC代码: #include <iostream> #include <algor

最长单调递增子序列问题

题目:设计一个 O( n ^ 2 )复杂度的算法,找出由 n 个数组成的序列的最长单调递增子序列. import java.util.Scanner; public class Main { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); while (scanner.hasNext()) { int n = scanner.nextInt(); int[] nums = new

求最长单调递增子序列

//求最长单调递增子序列 #include<stdio.h> #define MAXN 20 void disp(int a[],int b[],int k){ int i; for(i=k-1;i>0;i--){ if(b[k] == b[i]+1 && a[i] <= a[k]){ disp(a,b,i); break; } } printf("%d ",a[k]); } int maxL(int b[],int n){ //求数组b中最大值

LIS(nlogn) POJ 3903 Stock Exchange

题目传送门 1 /* 2 最长递增子序列O(nlogn)算法: 3 设当前最长递增子序列为len,考虑元素a[i]; 4 若d[len]<a[i],则len++,并将d[len]=a[i]; 5 否则,在d[1~len]中二分查找,找到第一个比它小的元素d[j],并d[j]=a[i]. 6 */ 7 #include <cstdio> 8 #include <cstring> 9 #include <iostream> 10 #include <algori

POJ 3903 Stock Exchange(LIS)

Stock Exchange Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 4578   Accepted: 1618 Description The world financial crisis is quite a subject. Some people are more relaxed while others are quite anxious. John is one of them. He is very

最长单调递增子序列——动态规划

题目描述: 给定一个序列X[0···n],找出它的最长的单调递增子序列(Longest Increasing Subsequence) 解题思路: 使用动态规划方法. 对于i= 1, 2, --,n,考虑以xi作为最后项的最长递增子序列的长度C[i]. 如果在xi项前面存在xj < xi , 那么 C[i] = max{C[j]} +1:否则,C[i] = 1. 因此, C[i] = max{C[j]} + 1, 存在j,1<=j<i, xj<xi C[i] = 1, 所有j,1&