- 给定一个长度为N的数组,找出一个最长的单调自增子序列(不一定连续,但是顺序不能乱)。
- 从一列数中筛除尽可能少的数使得从左往右看,这些数是从小到大再从大到小的(网易)。
1、思路:
定义L(i):以ai为最后一个元素的最长子序列的长度。当i=1或ai<min{a1,a2,...,a(i-1)}时,L(i)=1; 当1≤j≤i-1且aj<ai时,L(i)=max{L(j)}+1。时间复杂度O(n2)。
LIS
1 #include <iostream> 2 using namespace std; 3 #define len(a) (sizeof(a) / sizeof(a[0])) //数组长度 4 int lis(int arr[], int len) 5 { 6 int longest[len]; 7 for (int i=0; i<len; i++) 8 longest[i] = 1; 9 10 for (int j=1; j<len; j++) { 11 for (int i=0; i<j; i++) { 12 if (arr[j]>arr[i] && longest[j]<longest[i]+1){ //注意longest[j]<longest[i]+1这个条件,不能省略。 13 longest[j] = longest[i] + 1; //计算以arr[j]结尾的序列的最长递增子序列长度 14 } 15 } 16 } 17 18 int max = 0; 19 for (int j=0; j<len; j++) { 20 cout << "longest[" << j << "]=" << longest[j] << endl; 21 if (longest[j] > max) max = longest[j]; //从longest[j]中找出最大值 22 } 23 return max; 24 } 25 26 int main() 27 { 28 int arr[] = {1, 4, 5, 6, 2, 3, 8}; //测试数组 29 int ret = lis(arr, len(arr)); 30 cout << "max increment substring len=" << ret << endl; 31 return 0; 32 }
或者定义:B[i]来标记长度为i的增序列的最小数字是多少,遍历原数组对每一个数字插入到B[i]中的合适位置,并记录当前最长len。时间复杂度O(nlogn)。
BiSearchLIS
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 5 #define N 9 //数组元素个数 6 int array[N] = {2, 1, 6, 3, 5, 4, 8, 7, 9}; //原数组 7 int B[N]; //在动态规划中使用的数组,用于记录中间结果,其含义三言两语说不清,请参见博文的解释 8 int len; //用于标示B数组中的元素个数 9 10 int LIS(int *array, int n); //计算最长递增子序列的长度,计算B数组的元素,array[]循环完一遍后,B的长度len即为所求 11 int BiSearch(int *b, int len, int w); //做了修改的二分搜索算法 12 13 int main() 14 { 15 printf("LIS: %d\n", LIS(array, N)); 16 17 int i; 18 for(i=0; i<len; ++i) 19 { 20 printf("B[%d]=%d\n", i, B[i]); 21 } 22 23 return 0; 24 } 25 26 int LIS(int *array, int n) 27 { 28 len = 1; 29 B[0] = array[0]; 30 int i, pos = 0; 31 32 for(i=1; i<n; ++i) 33 { 34 if(array[i] > B[len-1]) //如果大于B中最大的元素,则直接插入到B数组末尾 35 { 36 B[len] = array[i]; 37 ++len; 38 } 39 else 40 { 41 pos = BiSearch(B, len, array[i]); //二分查找需要插入的位置 42 B[pos] = array[i]; 43 } 44 } 45 46 return len; 47 } 48 49 //修改的二分查找算法,返回数组元素需要插入的位置。 50 int BiSearch(int *b, int len, int w) 51 { 52 int left = 0, right = len - 1; 53 int mid; 54 while (left <= right) 55 { 56 mid = left + (right-left)/2; 57 if (b[mid] > w) 58 right = mid - 1; 59 else if (b[mid] < w) 60 left = mid + 1; 61 else //找到了该元素,则直接返回 62 return mid; 63 } 64 return left;//数组b中不存在该元素,则返回该元素应该插入的位置 65 }