单调递增子序列(二)

来源:互联网 发布:js 出生日期计算年龄 编辑:程序博客网 时间:2024/04/28 21:59

单调递增子序列(二)

时间限制:1000 ms  |  内存限制:65535 KB
难度:4
描述

给定一整型数列{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型整数)!
输出
对于每组测试数据输出整形数列的最长递增子序列的长度,每个输出占一行。
样例输入
7
1 9 10 5 11 2 13
2
-1
2
样例输出
5
1


题目耽搁很久才做出来,当时并没有理解网上的题解(二分法),现在就说一下自己的理解.

如果数据规模不大的话,就是一个简单的最长递增子序列, but 现在数据量太大.

这里使用二分法目的不仅仅查找还要增加序列的长度.

主要说一下二分法的使用, 假设dp[]存放结果(不是长度), num[] 存放输入的数据, 
当n = 1 时, 长度len = 0 (从1开始也一样), dp[0] = num[0], 下面进行二分法,
num[]从2---n, 每次都要在dp[]里面查找如果出现了,不用管, 如果num[i]没有在dp[]出现, 
那么就应该把num[i]添加到dp[]里面, 这里的添加也是有讲究的,如果num[i] > All(dp), 那么
就在dp[]最后添加,长度加一, 否则就把刚好比num[i]大(即前一个比num[i]小)的替换掉,
目的就是让已经有的序列所有元素最小化,以便为后面的数据提供方便.

二分法一定要写对,仔细考虑找不到要返回什么.

代码
#include <stdio.h>#include <string.h>int a[100002];int dp[100002];int len;int bin(int x){int mid, left = 0,right = len; // 记得初始化 while (left <= right){mid = (left + right) / 2;if (dp[mid] == x){return mid;}else if (dp[mid] < x){left = mid + 1;}else{right = mid - 1;}}return left;// 返回刚好的坐标(dp[left-1] < x) }int main(){int i, j, n, res;while (~scanf("%d", &n)){memset(dp, 0, sizeof(dp));for (i=0; i<n; ++i)scanf("%d", &a[i]);dp[0] = a[0];// 先保存第一个数 len = 0;for (i=1; i<n; ++i){res = bin(a[i]);dp[res] = a[i];if (len < res)// a[i] > dp[len] 增加了元素 len++;}printf("%d\n", len+1);// len 从 0 开始 }return 0;} 


0 0
原创粉丝点击