假设我们给出了一个整数输入数组,如何找到满足以下条件的最长凸子序列:
c[i] < (c[i-1] + c[i+1]) / 2
c[i-1]
,c[i]
和c[i+1]
是子序列中的三个连续元素。
例如,如果输入数组为{ 1, 2, -1, 0, 3, 8, 5 }
,则最长的凸子序列应为:{ 1, -1, 0, 3, 8 }
或{ 2, -1, 0, 3, 8 }
。
我尝试使用“最长增加子序列”(LIS)问题中的相同动态编程思想来解决此问题。但是因为子序列中的每个元素都依赖于前两个元素,所以似乎O(n ^ 2)解决方案是不可能的。谢谢您的帮助。
答案 0 :(得分:4)
这是O(N 2 log N)算法(由于log N
只是因为排序我们可以将其减少为O(N 2 使用不同的排序算法或更高级的优先级队列来记录日志N)或甚至O(N 2 ):
P[]
。每对内的元素按其索引排序。Y2 - Y1
对这些对进行排序。如果值相等Y2 - Y1
,则应按降序排列第二个索引。L[]
。P[]
的每对(按排序顺序):L[X2] = max(L[X2], L[X1] + 1)
。其中X
是输入数组中元素的索引。L[]
中找到最大值。这是最长凸子序列的长度。L[X2]
后,我们将创建一个节点,该节点指向由X1
对应的条目指向的节点,然后将与X2
对应的条目指向此新节点:BP_Head[X2] = new Node(BP_Head[X1])
。 凸性属性c[i] < (c[i-1] + c[i+1]) / 2
可能会转换为等效的不等式c[i] - c[i-1] < c[i+1] - c[i]
。这意味着在按排序顺序处理对时,我们不再需要检查凸属性。所以第4步的唯一任务就是增加子串。
该算法的简化变体需要O(N 2 )空间。如果不使用大型数组P[]
,我们会使用输入数组S[]
的预排序副本以及优先级队列来减少空间复杂性。步骤#4从该优先级队列的顶部接收元素。为了保持此优先级队列的大小等于N,我们可以仅在删除S[i+1] - S[j]
之后将元素S[i] - S[j]
推送到队列(因此队列仅为每个j
保留一个元素)。如果我们使用已知的DP技巧来存储指向原始后向指针链的“中间”的一个后向指针(对于每个索引)(然后递归地重复该算法),则不需要后向指针林消耗的巨大空间对于这个“中间”后指针之前和之后的两个子数组。
和O(N 3 )算法:
该图具有O(N 2 )顶点和O(N 3 )边。它可以用O(N 3 )时间构建;由于它是一个DAG,找到最长路径也需要O(N 3 )时间。
答案 1 :(得分:4)
让我们将最长的凸序列称为LCS; N> 1的最小可能长度是2.下面的算法是不言自明的。
int LCS[N];
LCS[0] = 1; LCS[1] =2 ;
for(i=2;i<N;i++)
{
LCS[i] = 2;
for(j=1;j<i;j++)
{
for(k=0;k<j;k++)
{
if(LCS[j]-1 == LCS[k] && A[j] < (A[k] + A[i])/2 )
LCS[i] = max( LCS[i], 1+LCS[j]);
}
}
}