寻找覆盖所有线段的最少点数

Finding minimum number of points to cover all segments

您好,我有如下问题:

给定一组 n 个片段 {[a0, b0], [a1, b1], . . . , [an-1, bn-1]} 一条直线上有整数坐标,求最少m个点使得每段至少包含一个点。即,找到一组最小大小的整数 X,使得对于任何段 [ai,bi] 都有一个点 x ∈ X 使得 ai ≤ x ≤ bi.

输入格式:输入的第一行包含段数n。以下n行中的每一行包含两个整数ai和bi(由space分隔)定义第i个段的端点坐标。

输出格式:第一行输出最少m个点,第二行输出m个点的整数坐标(间隔spaces)。您可以按任何顺序输出点。如果有很多这样的点集,你可以输出任何一组。 (不难看出,总存在一组最小尺寸的点,使得所有点的坐标都是整数。)

Sample 1:
Input: 3
1 3
2 5
3 6
Output: 1 3
Explanation:
In this sample, we have three segments: [1,3],[2,5],[3,6] (of length 2,3,3 respectively). All of them contain the point with coordinate 3: 1 ≤3 ≤3, 2 ≤3 ≤5, 3 ≤ 3 ≤ 6.
Sample 2:
Input: 4
4 7
1 3
2 5
5 6
Output: 2
3 6

说明: 第二和第三段包含坐标为 3 的点,而第一和第四段包含坐标为 6 的点。所有四个段都不能被单个点覆盖,因为段 [1, 3] 和 [5, 6] 是不相交的。

解决方法: 贪婪的选择是选择最小的右端点。然后 删除包含该端点的所有段 继续选择最小右端点并删除线段

我遵循了解决方案。我找到了最小的右端点,删除了我代码中包含该端点的所有段。然后使用新的段列表再次执行该函数(继续选择最小右端点并删除段 - 递归)但我坚持我的代码顺序并且无法使其工作。

list_time = [[4,7],[1,3],[2,5],[5,6]]

def check_inside_range(n, lst): #Function to check if a number is inside the range of start and end of a list
#for example 3 is in [3,5], 4 is not in [5,6], return False if in
    if lst[1]-n>=0 and n-lst[0]>=0:
        return False
    else:
        return True
def lay_chu_ki(list_time):

    list_time.sort(key = lambda x: x[1]) #Sort according to the end of each segments [1,3],[2,5],[5,6],[4,7]
    first_end = list_time[0][1] #Selecting the minimum right endpoint
    list_after_remove = list(filter(lambda x: check_inside_range(first_end, x),list_time))
    #Remove all segments that contains that endpoint

    lay_chu_ki(list_after_remove) #Keep doing the function again with new segments list
                                  #(Keep choosing minimum right endpoint and removing segments)

    return first_end #I don't know where to put this line.

print(lay_chu_ki(list_time))

如您所见,我已经完成了 3 个步骤:选择最小右端点删除包含该端点的所有段继续选择最小右端点并删除线段,但它不会以某种方式起作用。我尝试先打印两个数字 3 和 6(每个递归调用的 return 结果)。我还尝试创建一个 count 变量来计算每个递归调用 (count +=1) 但它也不起作用,因为它会为每个调用重置 count = 0

我认为递归使实现过于复杂。虽然它仍然可行,但您必须传递一堆额外的参数,这可能很难跟踪。在我看来,迭代地实现这种方法要简单得多。

此外,您的方法重复使用 filter()list(),每次执行都需要线性时间(澄清一下,“线性”表示输入列表的大小呈线性)。在最坏的情况下,您会对列表中的每个元素执行该操作,这意味着您的原始实现的运行时间是二次的(假设您修复了代码中的现有问题)。这种方法通过单次遍历列表来避免这种情况:

def lay_chu_ki(list_time):
    list_time.sort(key=lambda x: x[1])
    idx = 0
    selected_points = []
    
    while idx != len(list_time):
        selected_point = list_time[idx][1]
        
        while idx != len(list_time) and list_time[idx][0] <= selected_point:
            idx += 1
        
        selected_points.append(selected_point)
    
    return selected_points
        

result = lay_chu_ki(list_time)
print(len(result))
print(' '.join(map(str, result)))

对于给定的列表,输出:

2
3 6