我在一本书中找到了LIS的代码,我不太能够证明正确性的证据。有人可以帮我解决这个问题。如果新元素不是插入新元素的最大值,那么所有代码都在删除集合中新插入元素旁边的元素。
set<int> s;
set<int>::iterator it;
for(int i=0;i<n;i++)
{
s.insert(arr[i]);
it=s.find(arr[i]);
it++;
if(it!=s.end())
s.erase(it);
}
cout<<s.size()<<endl;
n是序列的大小,arr是序列。如果我们不必找到“严格”增加的序列,我不认为以下代码将起作用。我们是否可以修改代码以查找允许相等的增加序列。
编辑:该算法仅在输入不同时才有效。
答案 0 :(得分:3)
LIS有几种解决方案。 最典型的是使用动态编程的O(N ^ 2)算法,其中对于每个索引,您计算“在索引i处结束的最长增加序列”。 您可以使用聪明的数据结构或二进制搜索将其加速到O(N log N)。
您的代码会绕过此代码并仅计算LIS的长度。 考虑输入“1 3 4 5 6 7 2”,最后设置的内容为“1 2 4 5 6 7”,这不是LIS,但长度是正确的。 证据应该使用归纳法如下:
在第i次迭代之后,第j个最小元素是数组的前i个元素中长度j的递增序列的最小可能结束。
考虑输入“1 3 2”。在第二次迭代之后我们设置“1 3”,因此1是长度1的增加序列的最小可能结束,3是长度2的增加序列的最小可能结束。
在第三次迭代之后,我们设置了“1 2”,其中2是长度为2的递增序列的最小可能结束。
我希望你能自己做归纳步骤:)
答案 1 :(得分:2)
代码是LIS的O(nlogn)解决方案,但是你想找到非严格增加的序列,实现有问题,因为std :: set不允许重复元素。这是有效的代码。
#include <iostream>
#include <set>
#include <algorithm>
using namespace std;
int main()
{
int arr[] = {4, 4, 5, 7, 6};
int n = 5;
multiset<int> s;
multiset<int>::iterator it;
for(int i=0;i<n;i++)
{
s.insert(arr[i]);
it = upper_bound(s.begin(), s.end(), arr[i]);
if(it!=s.end())
s.erase(it);
}
cout<<s.size()<<endl;
return 0;
}
答案 2 :(得分:1)
证明相对简单:将s
设置为排序列表。我们可以用loop invariant证明它。在算法的每次迭代之后,s[k]
包含arr
的最小元素,该元素在子数组中从零到{{1}的最后一个元素结束长度k
的递增子序列我们到目前为止已经考虑过了。我们可以通过归纳证明这一点:
arr
将只包含一个元素,这是一个元素的一个简单的升序序列。s
是目前为止找到的最大元素的情况下,它可以将其扩展为1,或者用arr[i]
替换现有元素,小于之前的元素。当发生集合的扩展时,会发生这种情况,因为当前元素arr[i]
可以附加到当前LIS。当替换发生在位置arr[i]
时,索引为k
,因为arr[i]
结束了长度为arr[i]
的递增子序列,并且小于或等于旧的k
,用于结束长度s[i]
之前的“最佳”升序子序列。
有了这个不变量,很容易看出k
包含尽可能多的元素,而不是整个s
耗尽后arr
的最长升序子序列。
答案 3 :(得分:-1)
问题陈述:
For A(n) :a0, a1,….an-1 we need to find LIS
Find all elements in A(n) such that, ai<aj and i<j.
For example: 10, 11, 12, 9, 8, 7, 5, 6
LIS will be 10,11,12
这是基于DP的O(N ^ 2)解决方案。
1 Finding SubProblems
Consider D(i): LIS of (a0 to ai) that includes ai as a part of LIS.
2 Recurrence Relation
D(i) = 1 + max(D(j) for all j<i) if ai > aj
3 Base Case
D(0) = 1;
查看代码的链接: https://innosamcodes.wordpress.com/2013/07/06/longest-increasing-subsequence/