使用动态编程查找最大间隔

时间:2014-02-15 14:36:48

标签: algorithm dynamic-programming

http://www.cs.uiuc.edu/~jeffe/teaching/algorithms/notes/05-dynprog.pdf 我正在为练习做这些问题,在那里我遇到了一个困扰我的问题。

  

7.(a)假设我们在平面中给出了一组L个n个线段,其中每个线段在y = 0的线上有一个端点,并且一个端点   在y = 1行,所有2n个端点都是不同的。描述和   分析算法来计算L的最大子集,其中没有   一对段相交。

     

(b)假设我们在平面上给出了一组n个线段,   其中每个段的端点位于单位圆上x 2 + y 2 =   1,所有2n端点都是不同的。描述和分析一个   算法计算L的最大子集,其中没有对   段相交。

我在O(n log n)时间内想出了如何做7a(问题是找到增加数字的最大子集的伪装问题)。我几乎接近放弃7b,因为我无法找到一种方法来做到这一点。

然而,有没有办法将7b的前提转换为更像7a的东西?我觉得这是解决问题的正确方法,任何帮助解决这个问题都会非常感激。

3 个答案:

答案 0 :(得分:1)

我无法想出一个O(n * log(n))算法,但这里有一个O(n 2 )算法。

我们的想法是构建一个有向图,其顶点表示给定集合中的段,而边缘表示“位于右侧”关系。

设L为段列表:{(a 1 ,b 1 ),(a 2 ,b 2 ),...,(a n ,b n )},其中 k 和b k < / sub>是第k个分段的端点。

设L'为段列表:{(a 1 ,b 1 ),(b 1 ,a 1 ),(a 2 ,b 2 ),(b 2 ,a 2 ) ,...,(a n ,b n ),(b n n )}。

让图的顶点具有从1到2 * n的索引,每个索引k表示段L'[k],即(a k / 2 ,b k / 2 )如果k是奇数,并且(b k / 2 ,a k / 2 ),如果k是偶数。

段(a 1 ,b 1 )据说位于段的右侧( 2 ,b 2 )当a 1 ,a 2 ,b 2 ,b 1 时按顺时针顺序放置在单位圆上。

注意1)如果一个段位于另一个段的右侧,则它们不相交; 2)如果L的两个段不相交,则L'的四个相应段中的两个必然位于另一个的右侧; 3)来自L的任何一组非相交段由L'的一系列段定义,每段都位于前一段的右侧。

Four non-intersecting segments from L; Eight corresponding segments from L'; Four segments from L' found by the algorithm, ordered from left to right

算法概要:

for every k1 from 1 to 2*n:
    for every k2 from 1 to 2*n:
        if (L'[k1].a, L'[k1].b) lies to the right of (L'[k2].a, L'[k2].b):
            add a directed edge (k1, k2) to the graph 
Find the longest path in the graph: (k1, k2, ..., km). 
The answer to the problem is: (k1/2, k2/2, ..., km/2).

答案 1 :(得分:1)

这是一个O(n 2 )算法。

我们在圈子上有 2n 个端点。选择任意一点,然后从顺时针方向的1开始逐渐增加顺序标记点。因此,我们有从 1 2n 标记的点。因此,任何线段 l 都可以表示为(i,j),其中 i j 是端点 l 1≤i&lt;j≤2n

L i,j L 的子集,使 l =(a,b)∈L i,j 如果i≤a&lt; b≤j。另外,将 D [i,j] 定义为1≤i≤j≤2n作为 L i中非交叉线的最大数量, Ĵ 的。我们需要找到 D [1,2n]

我们在这里使用动态编程。将 D [i,i] 初始化为 0 ,将 D [i,i + 1] 初始化为 1 if( i,i + 1)是一个线段,否则将其设置为 0 。我们可以使用以下代码构建D:

if (i,j) is a line segment :
     D[i,j] = D[i+1,j-1]+1
else if (i,k) is a line segment and i<k<j :
     D[i,j] = max(D[i,j] , D[i+1,k-1]+D[k+1,j]+1)
else if (k,j) is a line segment and i<k<j :
     D[i,j] = max(D[i,j] , D[i,k-1]+D[k+1,j-1]+1)
else :
     D[i,j] = max(D[i,j] , D[i+1,j-1])

由于D占用 O(n 2 空间,我们在恒定时间内计算 D 的每个单元格,算法的时间复杂度为为O(n 2

参考: http://www.cs.toronto.edu/~robere/csc373h/files/A2-sol.pdf

在标题线路交叉点(Redux)

下查看

答案 2 :(得分:0)

创建两个列表。第一个列表具有通过从(1,0)开始围绕圆逆时针扫描而创建的排序。标记的段的第一个端点已标记。第二个列表的顺序相同,但顺时针绕圆圈顺序排列。通过这种方式,您现在有两个列表,其中段端点显示交叉的排序。例如,如果第一个列表中的第一个点在第二个列表中没有第一个对应的端点,那么它将与第二个列表中出现在它之前的所有段相交。 (这里需要注意的是,一行的两个点都可以在段的末尾之前。一个简单的检查就可以消除这一点。)然后,您可以运行该列表。这种方法的总复杂性似乎是每个列表创建的O(n log n)和运行列表的O(n)。