Algorithm O(nlgn)中的最长非减缩子序列
我有下面的算法,效果很好 我试着在这里为自己解释,这里也有解释,在stackoverflow上也有解释 我想修改它以产生最长的非单调递增子序列 对于序列30 20 10 10 10 答案应该是4:“10” 但是nlgn版本的算法不起作用。初始化s以包含第一个元素“30”,并从第二个元素开始=20。情况就是这样:Algorithm O(nlgn)中的最长非减缩子序列,algorithm,lis,Algorithm,Lis,我有下面的算法,效果很好 我试着在这里为自己解释,这里也有解释,在stackoverflow上也有解释 我想修改它以产生最长的非单调递增子序列 对于序列30 20 10 10 10 答案应该是4:“10” 但是nlgn版本的算法不起作用。初始化s以包含第一个元素“30”,并从第二个元素开始=20。情况就是这样: 第一步:30不大于或等于20。我们发现最小的元素大于20。新的s变成“20” 第二步:20大于或等于20。我们扩展序列,s现在包含“20” 第三步:10不大于或等于20。我们发现大于10
int height[100];
int s[100];
int binary_search(int first, int last, int x) {
int mid;
while (first < last) {
mid = (first + last) / 2;
if (height[s[mid]] == x)
return mid;
else if (height[s[mid]] >= x)
last = mid;
else
first = mid + 1;
}
return first; /* or last */
}
int longest_increasing_subsequence_nlgn(int n) {
int i, k, index;
memset(s, 0, sizeof(s));
index = 1;
s[1] = 0; /* s[i] = 0 is the index of the element that ends an increasing sequence of length i = 1 */
for (i = 1; i < n; i++) {
if (height[i] >= height[s[index]]) { /* larger element, extend the sequence */
index++; /* increase the length of my subsequence */
s[index] = i; /* the current doll ends my subsequence */
}
/* else find the smallest element in s >= a[i], basically insert a[i] in s such that s stays sorted */
else {
k = binary_search(1, index, height[i]);
if (height[s[k]] >= height[i]) { /* if truly >= greater */
s[k] = i;
}
}
}
return index;
}
int高度[100];
int s[100];
int二进制搜索(int first,int last,int x){
int mid;
while(第一次<最后一次){
中间=(第一个+最后一个)/2;
如果(高度[s[mid]]==x)
中途返回;
否则,如果(高度[s[mid]]>=x)
最后=中期;
其他的
第一个=中间+1;
}
先返回;/*或最后返回*/
}
整数最长\u递增\u子序列\u nlgn(整数n){
int i,k,索引;
memset(s,0,sizeof(s));
指数=1;
s[1]=0;/*s[i]=0是结束长度i=1的递增序列的元素的索引*/
对于(i=1;i=height[s[index]]){/*较大的元素,则扩展序列*/
index++;/*增加我的子序列的长度*/
s[index]=i;/*当前玩偶结束我的子序列*/
}
/*否则在s>=a[i]中找到最小的元素,基本上在s中插入一个[i],使s保持排序*/
否则{
k=二进制搜索(1,索引,高度[i]);
如果(高度[s[k]]>=高度[i]){/*如果真的>=更大*/
s[k]=i;
}
}
}
收益指数;
}
要查找最长的非严格递增子序列,请更改以下条件:
A[i]
在活动列表的所有候选端中最小,我们将启动长度1
的新活动列表A[i]
在活动列表的所有候选端中最大,我们将克隆最大的活动列表,并通过A[i]
对其进行扩展A[i]
介于两者之间,我们将找到一个具有最大结束元素的列表,该元素小于A[i]
。通过A[i]
克隆并扩展此列表。我们将丢弃与此修改列表长度相同的所有其他列表A[i]
小于活动列表中所有候选端中的最小值,我们将启动长度1
的新活动列表A[i]
在活动列表的所有候选端中最大,我们将克隆最大的活动列表,并通过A[i]
对其进行扩展A[i]
介于两者之间,我们将找到一个列表,其中最大的结束元素小于或等于A[i]
。通过A[i]
克隆并扩展此列表。我们将丢弃与此修改列表长度相同的所有其他列表10
不小于10
(最小元素)。我们找到小于或等于10
(即s[0]==10
)的最大元素。通过10
克隆并扩展此列表。放弃长度为2的现有列表。新的s
变为{10}
此问题的完全不同的解决方案如下。制作数组的副本并对其排序。然后,计算数组任意两个元素之间的最小非零差(这将是两个相邻数组元素之间的最小非零差),并将其称为δ。此步骤需要时间O(n log n)
关键的观察结果是,如果将0添加到原始数组的元素0,将δ/n添加到原始数组的第二个元素,将2δ/n添加到数组的第三个元素,等等,那么原始数组中的任何非减缩序列都将成为新数组中的严格递增序列,反之亦然。因此,可以这样变换阵列,然后运行标准的最长递增子序列解算器,该解算器在时间O(n log n)内运行。这个过程的最终结果是一个O(n logn)算法,用于寻找最长的非减缩子序列
例如,考虑30, 20, 20、10, 10, 10、10。在这种情况下,δ=10,n=7,因此δ/n&近似值;1.42. 然后,将创建新阵列
40, 21.42, 22.84, 14.28, 15.71, 17.14, 18.57
在这里,LIS是14.28、15.71、17.14、18.57,映射回原始数组中的10、10、10、10
希望这有帮助 除了
二进制搜索()函数中的一个问题外,您的代码几乎可以正常工作。
函数应该返回大于目标元素(x)的第一个元素的索引,因为您需要最长的非递减序列。把它改成这个,就可以了
如果使用C++,<>代码> STD::LoeRyBangEnter()/<代码>和<>代码> STD::UpPixBoin()/Cuff>将帮助您摆脱这个困惑的问题。顺便说一下,if语句“if(height[s[k]>=height[i])
”是多余的
int binary_search(int first, int last, int x) {
while(last > first)
{
int mid = first + (last - first) / 2;
if(height[s[mid]] > x)
last = mid;
else
first = mid + 1;
}
return first; /* or last */
}
通过使用字典比较,只需将最长递增子序列算法应用于有序对(A[i],i)。我的Java版本:
public static int longestNondecreasingSubsequenceLength(List<Integer> A) {
int n = A.size();
int dp[] = new int[n];
int max = 0;
for(int i = 0; i < n; i++) {
int el = A.get(i);
int idx = Arrays.binarySearch(dp, 0, max, el);
if(idx < 0) {
idx = -(idx + 1);
}
if(dp[idx] == el) { // duplicate found, let's find the last one
idx = Arrays.binarySearch(dp, 0, max, el + 1);
if(idx < 0) {
idx = -(idx + 1);
}
}
dp[idx] = el;
if(idx == max) {
max++;
}
}
return max;
}
public static int最长不减短子序列长度(列表A){
int n=A.size();
int dp[]=新的int[n];
int max=0;
对于(int i=0;i