此前在动态规划一讲:动态规划(3)-最长递增子序列 曾说过此问题,当前是的双重循环是O(n^2)的复杂度。
后来在网上看到说LIS问题有O(nlogn)的算法,于是拿来小研究了一下。
这个算法其实已经不是DP了,有点像贪心。至于复杂度降低其实是因为这个算法里面用到了二分搜索。本来有N个数要处理是O(n),每次计算要查找N次还是O(n),一共就是O(n^2);现在搜索换成了O(logn)的二分搜索,总的复杂度就变为O(nlogn)了。
这个算法的具体操作如下(by RyanWang):
开一个栈,每次取栈顶元素top和读到的元素temp做比较,如果temp > top 则将temp入栈;如果temp < top则二分查找栈中的比temp大的第1个数,并用temp替换它。 最长序列长度即为栈的大小top。
这也是很好理解的,对于x和y,如果x < y且Stack[y] < Stack[x],用Stack[x]替换Stack[y],此时的最长序列长度没有改变但序列Q的”潜力”增大了。
举例:原序列为1,5,8,3,6,7
栈为1,5,8,此时读到3,用3替换5,得到1,3,8; 再读6,用6替换8,得到1,3,6;再读7,得到最终栈为1,3,6,7。最长递增子序列为长度4。
用该算法完成POJ2533的具体代码如下:
#include <iostream> #define SIZE 1001 using namespace std; int main() { int i, j, n, top, temp; int stack[SIZE]; cin >> n; top = 0; /* 第一个元素可能为0 */ stack[0] = -1; for (i = 0; i < n; i++) { cin >> temp; /* 比栈顶元素大数就入栈 */ if (temp > stack[top]) { stack[++top] = temp; } else { int low = 1, high = top; int mid; /* 二分检索栈中比temp大的第一个数 */ while(low <= high) { mid = (low + high) / 2; if (temp > stack[mid]) { low = mid + 1; } else { high = mid - 1; } } /* 用temp替换 */ stack[low] = temp; } } /* 最长序列数就是栈的大小 */ cout << top << endl; //system("pause"); return 0; }
这其中用到了二分查找第一个大于等于的,其实C++里面的有一个函数可用代替二分。
lower_bound 函数
下面是使用lower_bound优化最长上升子序列。由于长度相同的上升子序列只需要保存结尾最小的那个,而长度递增时,结尾数字的大小也是递增的。最长上升子序列就是找出比他大的第一个数。前面的数都比他小,所以他和这个数的长度相同。然后由于他比较然后小,更新找到的那个值。
#include<stdio.h> #include<string.h> #include<algorithm> using namespace std; int num[10]={3,6,3,2,4,6,7,5,4,3}; const int INF=0x3f3f3f3f; int l=10; int g[100]; int d[100]; int main() { fill(g,g+l,INF); int max_=-1; for(int i=0;i<l;i++) { int j=lower_bound(g,g+l,num[i])-g; d[i]=j+1; if(max_<d[i]) max_=d[i]; g[j]=num[i]; } printf("%d\n",max_); return 0; } 此前在动态规划一讲:动态规划(3)-最长递增子序列 曾说过此问题,当前是的双重循环是O(n^2)的复杂度。 后来在网上看到说LIS问题有O(nlogn)的算法,于是拿来小研究了一下。 这个算法其实已经不是DP了,有点像贪心。至于复杂度降低其实是因为这个算法里面用到了二分搜索。本来有N个数要处理是O(n),每次计算要查找N次还是O(n),一共就是O(n^2);现在搜索换成了O(logn)的二分搜索,总的复杂度就变为O(nlogn)了。 这个算法的具体操作如下(by RyanWang): 开一个栈,每次取栈顶元素top和读到的元素temp做比较,如果temp > top 则将temp入栈;如果temp < top则二分查找栈中的比temp大的第1个数,并用temp替换它。 最长序列长度即为栈的大小top。 这也是很好理解的,对于x和y,如果x < y且Stack[y] < Stack[x],用Stack[x]替换Stack[y],此时的最长序列长度没有改变但序列Q的”潜力”增大了。 举例:原序列为1,5,8,3,6,7 栈为1,5,8,此时读到3,用3替换5,得到1,3,8; 再读6,用6替换8,得到1,3,6;再读7,得到最终栈为1,3,6,7。最长递增子序列为长度4。 用该算法完成POJ2533的具体代码如下: #include <iostream> #define SIZE 1001 using namespace std; int main() { int i, j, n, top, temp; int stack[SIZE]; cin >> n; top = 0; /* 第一个元素可能为0 */ stack[0] = -1; for (i = 0; i < n; i++) { cin >> temp; /* 比栈顶元素大数就入栈 */ if (temp > stack[top]) { stack[++top] = temp; } else { int low = 1, high = top; int mid; /* 二分检索栈中比temp大的第一个数 */ while(low <= high) { mid = (low + high) / 2; if (temp > stack[mid]) { low = mid + 1; } else { high = mid - 1; } } /* 用temp替换 */ stack[low] = temp; } } /* 最长序列数就是栈的大小 */ cout << top << endl; //system("pause"); return 0; }
相关推荐
在算法领域,最长上升子序列(Longest Increasing Subsequence,简称LIS)是一个经典问题,它涉及到找出一个给定序列中严格单调递增的子序列,并返回这个子序列的最大长度。LIS问题不仅是计算机科学中的一个基础问题...
最终,dp数组中的最大值即为整个序列的最长上升子序列长度。 LeetCode上的这个问题通常以题目形式出现,用户需要提交代码来解决这个问题。题目编号可能是20060w50,这可能是一个模拟编号,具体的编号会因LeetCode的...
优化的关键在于,在更新最长上升子序列长度的同时,记录该长度所对应的序列的最后一个元素的位置。使用一个辅助数组来维护长度为`i`的上升子序列的最小末尾值,并对每个元素进行二分查找,这样可以得到一个时间...
最长递增子序列(Longest Increasing Subsequence, LIS)问题是一个经典的计算机科学问题,它在动态规划、算法设计和序列分析等领域都有广泛的应用。在这个C程序中,我们将深入探讨如何利用C语言来解决这个问题。 ...
描述中的 "求一组数的最长上升子序列,时间复杂度为O(nlogn)" 提到,我们需要找到一种高效的方法来解决这个问题,而这个方法的时间复杂度应该是线性对数级别的。这意味着我们不能简单地采用暴力枚举所有可能的子序列...
在LeetCode的第300题“最长上升子序列”中,目标是找出给定无序整数数组中的最长上升子序列(Longest Increasing Subsequence, LIS)的长度。这是一道经典的动态规划(Dynamic Programming, DP)和二分查找(Binary ...
在数据与算法的领域中,解决数列的最长递增子序列问题一直是一个核心议题。它不仅在理论上具有重要的地位,更在实际应用中发挥着关键作用。在EE课程中,我们通过实验报告的形式,深入探讨了如何通过动态规划方法来...
4. **最长上升子序列 (Longest Increasing Subsequence, LIS)** - LIS是给定序列中最长的子序列,其元素按递增顺序排列。 - 通过动态规划可以解决此问题,时间复杂度为O(nlogn)。 5. **最优三角剖分** - 在给定...
动态规划的一个经典例子是计算最长递增子序列(LIS),通过维护一个数组来记录到达每个位置的最长递增子序列的长度,最后通过构建最优解来得到最终结果。 贪心算法是一种在每一步选择中都采取在当前状态下最好或...
10. 最长递增子序列(LIS):LIS问题可以通过动态规划解决,时间复杂度为O(NlogN),空间复杂度为O(N)。答案是D。 11. 函数复杂度:函数F(n)的时间复杂度由递归公式决定,这里是O(n^2)。答案是D。 12. 整型计算:...