LIS表示O(NlogN)或O(Nlog ^2N)中的坐标值

LIS for coordinate values in O(NlogN) or O(Nlog^2N)

本文关键字:坐标 2N 表示 NlogN LIS Nlog      更新时间:2023-10-16

这是一个标准的动态编程问题LIS问题

我想要2D坐标中点的最长递增子序列

也就是说,如果(x1<=x2)&amp;(y1<=y2)&amp!(x1==x2&&y1==y2)&amp;(j>i)

我的代码如下,使用标准DP:-为O(N^2)

#include <vector>
#include <iostream>
#include <algorithm>

using namespace std;

struct Pair
{
int x;
int y;
};

int main()
{
int n;
cin>>n;
vector<Pair> arr;
int L[1000000];
Pair a;
int i;int Maxchain=0;
for(i=0;i<n;i++)
{
cin>>a.x>>a.y;
arr.push_back(a);
L[i]=0;
for (int j = i-1; j >=0; j--)
{
if ((L[j]>(Maxchain-1))&&(L[j]>=L[i])&&(arr[j].x <= arr[i].x) && (arr[j].y <= arr[i].y) && !(arr[j].x == arr[i].x && arr[j].y == arr[i].y))
L[i] = L[j]+1;

}
Maxchain = L[i]>Maxchain ?L[i]:Maxchain ;
}
cout<<Maxchain;
return 0;
}

这是一个O(N^2)解,它可以进一步简化吗?或者它在O(NlogN)或O(Nlog ^2 N)中求解的任何算法?

为了参考,在这里找到了一些东西:

具有两个数字的最长递增子序列(LIS)

第二个答案更适合我的情况,但我们如何实施呢?

需要更好的答案或算法。

我假设两个坐标都在[0..N-1]范围内(如果不是这样,我们可以在不改变它们的排序关系的情况下"压缩"它们)。

让我们仔细看看一个标准的动态编程解决方案。设f[i]是在i位置结束的最长递增子序列的长度。计算它的一种简单(但缓慢)的方法是过于迭代之前的所有元素并选择最优元素。我们想要找到的是对于p[j].x <= p[i].xp[j].y <= p[j].y这样的所有jmax f[j]。它看起来像是矩形中的某种二维查询(我知道还有另一个条件是p[j] != p[i],但我们可以通过查询两个矩形(p[i].x - 1, p[i].y)(p[i].x, p[i].y - 1)来解决它。)

因此,我们需要一个支持两种操作的数据结构:添加具有特定值的点和获得矩形中的最大值。按x坐标的分段树为其范围内的所有点存储按y坐标的平衡二进制搜索树,可以在每次查询的O(log^2 N)中执行。每个查询范围在树中最多分解为O(log N)个节点。如果是插入查询,我们需要将值为f[i]的当前点(p[i].x, p[i].y)插入到这些节点中每个节点的二进制搜索树中。如果是获取最大值查询,我们需要获取这些树中每个树的某个前缀的最大值。无论哪种方式,我们对每个查询的O(log N)二进制搜索树执行O(log N)操作。因此,总的时间复杂度是(N * log^2 N)。空间复杂度是O(N log N),因为树中有O(log N)级别,并且每个点每个级别最多可以出现一次。

这个解决方案已经满足了您的需求,但它看起来很难编码。我们可以稍微简化一下。我们可以进行两次"运行":在第一次运行期间,我们只存储进入段树每个节点的查询(到目前为止,我们不存储任何额外信息)。现在,我们可以保留节点中出现的所有数字的矢量和相同长度的二进制索引树,以跟踪每个前缀的最小值并有效地获取它(总体情况是:我们使用了预先知道所有查询的事实,因此我们可以使用排序矢量和二进制索引树的组合,而不是二进制搜索)。时间和空间复杂性分析与上述相同。

简单回顾一下:我们在O(N^2)动态编程解决方案中使用了一种支持矩形中最大查询和有效插入新点的数据结构,以加快为固定i找到最佳j,从而在O(N log^2 N)中解决该问题。