【DP入门】单调递增子序列(二)

来源:互联网 发布:武工队后勤部淘宝 编辑:程序博客网 时间:2024/05/16 10:41

题目来自于nyist第214题,如下:

描述

给定一整型数列{a1,a2...,an}(0<n<=100000),找出单调递增最长子序列,并求出其长度。

如:1 9 10 5 11 2 13的最长单调递增子序列是1 9 10 11 13,长度为5。

输入
有多组测试数据(<=7)
每组测试数据的第一行是一个整数n表示序列中共有n个整数,随后的下一行里有n个整数,表示数列中的所有元素.每个整形数中间用空格间隔开(0<n<=100000)。
数据以EOF结束 。
输入数据保证合法(全为int型整数)!
输出

对于每组测试数据输出整形数列的最长递增子序列的长度,每个输出占一行。


这题数据量达到了100000,用之前的O(n²)的方法会超时,我也是看了讨论区以及最优程序才知道要采用什么思路求解,DP方法大致是将上一个方法中将存储当前值是子序列第几个的数组改为存储长度为i的单调递增子序列的最小末尾值,比如序列 1 3 2,依次考虑1、3、2,考虑1时,dp[1] = 1;考虑3时,3比1大,dp[2] = 3;考虑2时,2比3小而比1大,故dp[2] = 2。以此方法,并且在查找最小末尾值时采用二分查找,最终将时间复杂度优化为O(nlogn)。


代码如下: 

#include <stdio.h>int dp[100000+5];int a[100000+5];int found(int num,int end){int top = 0;int mid;while(top <= end)    {        mid = (top+end)/2;        if(num > dp[mid])            top = mid+1;        else if(num < dp[mid])            end = mid-1;        else return mid;    }     return top;}int main(){int n,i,top,temp;do{top = -1; if(scanf("%d",&n) == EOF) break;    //输入为EOF,可以记下for(i = 0;i < n;i++){scanf("%d",&(a[i])); temp = found(a[i],top);top = top > temp ? top : temp;dp[temp] = a[i];}printf("%d\n",top+1); }while(1);return 0;} 

注意二分查找最好不要用递归形式,我之前就是涂方便写了递归,结果超时。像这种数据量大,很容易超时的题就有尽可能的优化,无论是在空间还是时间复杂度上。

0 0
原创粉丝点击