Algorithm 给定一组范围S和一个重叠范围R,找出S中与R相交的最小子集

Algorithm 给定一组范围S和一个重叠范围R,找出S中与R相交的最小子集,algorithm,range,dynamic-programming,Algorithm,Range,Dynamic Programming,以下是某个人向我提出的一个实践面试问题,我不确定最佳解决方案是什么: 给定一组范围: (例如S={(1,4),(30,40),(20,91),(8,10),(6,7),(3,9),(9,12),(11,14)},并给定一个目标范围R(例如R=(3,13)-表示从3到13的范围)。编写一个算法,以找到覆盖目标范围的最小范围集。该集合中的所有范围必须重叠,才能被视为跨越整个目标范围。(在本例中,答案为{(3,9)、(9,12)、(11,14)} 解决这个问题的最佳方法是什么?我想这应该用贪婪算法来实

以下是某个人向我提出的一个实践面试问题,我不确定最佳解决方案是什么:

给定一组范围:
(例如
S={(1,4),(30,40),(20,91),(8,10),(6,7),(3,9),(9,12),(11,14)}
,并给定一个目标范围R(例如
R=(3,13)
-表示从3到13的范围)。编写一个算法,以找到覆盖目标范围的最小范围集。该集合中的所有范围必须重叠,才能被视为跨越整个目标范围。(在本例中,答案为
{(3,9)、(9,12)、(11,14)}

解决这个问题的最佳方法是什么?我想这应该用贪婪算法来实现。在上面的例子中,我们会寻找所有与3相交的数字,然后从那些最大值最高的数字中选取。然后我们会对刚刚选取的数字做同样的事情。因此,既然我们选取了(3,9)我们现在想要找到所有与9相交的范围,在这些范围中,我们选择最大值最高的一个。在那个迭代中,我们选择了(9,12)。我们对那个范围做了同样的事情,我们发现下一个与12相交的范围,最大值最高的是(11,14)

在那个迭代之后,我们看到14大于13(我们范围的最大值),所以我们可以停止

这个算法的问题是,如何有效地查询相交范围?如果我们尝试线性搜索,我们最终得到的算法是
O(n^2)
。我的下一个想法是在每次运行循环时从列表中划掉任何相交范围。因此,在第一次迭代中,我们将(1,4)(3,9)交叉。在下一次迭代中,我们将(9,12)(3,9)(8,10)交叉所以在最后一次迭代中,我们所要看的就是{(30,40),(20,91),(6,7)}。我们还可以通过删除最小值大于13,最大值小于3的所有内容来提高效率。问题是,这可能还不够。在我们的范围内仍然存在大量重复序列的潜在问题。如果我们的范围列表包含类似于{(6,7)、(6,7)、(6,7)的内容,(6,7),(6,7)}我们每次都必须检查它们,即使它们对我们没有用处。即使我们只存储唯一的值(将它们全部放在一个集合中),我们可能有一个非常大的范围,有一系列的范围在我们的目标范围内,但我们也有一个范围在里面,几乎覆盖了整个目标范围


查询范围的有效方法是什么?或者,解决这个问题的更有效算法是什么?

使用区间树进行查询怎么样?()我不确定贪婪是否能在这里起作用。如果我们看最后一组选择,与
R
中的高点重叠,则这些选择中的每一个都有可能在早期选择之间重叠,例如:

R = (2,10) and we have (8,10) and (7,10) both overlapping with (6,8)
在这种情况下,我们只需要为
(6,8)
存储一个值作为路径的第二段;当我们在
R
中向低点绘制更长的路径时,再次访问
(6,8)
将是多余的,因为我们已经知道
(6,8)
访问时腿部计数较低。因此,您在访问时消除间歇的想法是有道理的。类似的方法可以奏效吗

leg = 1
start with the possible end (or beginning) intervals
label these intervals with leg
until end of path is reached:
  remove the intervals labeled leg from the tree
  for each of those intervals labeled leg:
    list overlapping intervals in the chosen direction
  leg = leg + 1
  label the listed overlapping intervals with leg

我可以推荐以下算法,其复杂性
O(nlogn)
不使用区间树

让我们介绍一些表示法。我们应该按间隔覆盖范围
(X,Y)
(X\u i,Y\u i)

第一次按起始点对给定间隔进行排序
(x\u i,y\u i)
。需要
O(n log n)


从区间<代码>(Xi i,yii i)<代码> > <代码> xi i> p>你的任务吸引了我,所以我写了一个C++程序,通过迭代覆盖目标范围左侧的范围来解决问题,递归地搜索覆盖剩余(右侧)的最小范围的范围。目标范围的一部分

对该算法的重大优化(本程序中未显示)将是,对于每个递归级别,使用与目标范围左侧重叠最大的范围,并从进一步考虑中丢弃与左侧重叠较小的所有范围。通过使用此规则,我相信最多会有一个递归调用树下降。这样的优化将生成一个复杂度为O(n log(n))的算法(n表示递归的深度,log(n)表示二进制搜索以找到重叠最多的范围)

此程序产生以下输出:

{ (3, 9) (9, 12) (11, 14) }

节目如下:

#include <utility>  // for std::pair
#include <vector>   // for std::vector
#include <iostream> // for std::cout & std::endl

typedef std::pair<int, int> range;
typedef std::vector<range> rangelist;

// function declarations
rangelist findRanges (range targetRange, rangelist candidateRanges);
void print (rangelist list);


int main()
{
    range target_range = { 3, 13 };

    rangelist candidate_ranges =
        { { 1, 4 }, { 30, 40 }, { 20, 91 }, { 8, 10 }, { 6, 7 }, { 3, 9 }, { 9, 12 }, { 11, 14 } };

    rangelist result = findRanges (target_range, candidate_ranges);

    print (result);
    return 0;
}


// Recursive function that returns the smallest subset of candidateRanges that
// covers the given targetRange.
// If there is no subset that covers the targetRange, then this function
// returns an empty rangelist.
//
rangelist findRanges (range targetRange, rangelist candidateRanges)
{
    rangelist::iterator it;
    rangelist smallest_list_so_far;

    for (it = candidateRanges.begin (); it != candidateRanges.end (); ++it) {

        // if this candidate range overlaps the beginning of the target range
        if (it->first <= targetRange.first && it->second >= targetRange.first) {

            // if this candidate range also overlaps the end of the target range
            if (it->second >= targetRange.second) {

                // done with this level - return a list of ranges consisting only of
                // this single candidate range
                return { *it };
            }
            else {
                // prepare new version of targetRange that excludes the subrange
                // overlapped by the present range
                range newTargetRange = { it->second + 1, targetRange.second };

                // prepare new version of candidateRanges that excludes the present range
                // from the list of ranges
                rangelist newCandidateRanges;
                rangelist::iterator it2;
                // copy all ranges up to but not including the present range
                for (it2 = candidateRanges.begin (); it2 != it; ++it2) {
                    newCandidateRanges.push_back (*it2);
                }
                // skip the present range
                it2++;
                // copy the remainder of ranges in the list
                for (; it2 != candidateRanges.end(); ++it2) {
                        newCandidateRanges.push_back (*it2);
                }

                // recursive call to find the smallest list of ranges that cover the remainder
                // of the target range not covered by the present range
                rangelist subList = findRanges (newTargetRange, newCandidateRanges);

                if (subList.size () == 0) {
                    // no solution includes the present range
                    continue;
                }
                else if (smallest_list_so_far.size () == 0 ||               // - first subList that covers the remainder of the target range
                         subList.size () < smallest_list_so_far.size ())    // - this subList is smaller than all previous ones checked
                {
                    // add the present range to the subList, which represents a solution
                    // (though possibly not optimal yet) at the present level of recursion
                    subList.push_back (*it);
                    smallest_list_so_far = subList;
                }
            }
        }
    }
    return smallest_list_so_far;
}

// print list of ranges
void print (rangelist list)
{
    rangelist::reverse_iterator rit;
    std::cout << "{ ";
    for (rit = list.rbegin (); rit != list.rend (); ++rit) {
        std::cout << "(" << rit->first << ", " << rit->second << ") ";
    }
    std::cout << "}" << std::endl;
}
#包含//用于std::pair
#include//for std::vector
#包含//用于std::cout和std::endl
typedef std::对范围;
typedef std::向量范围列表;
//函数声明
范围列表findRanges(范围目标范围、范围列表候选范围);
作废打印(范围列表);
int main()
{
范围目标_范围={3,13};
范围列表候选范围=
{ { 1, 4 }, { 30, 40 }, { 20, 91 }, { 8, 10 }, { 6, 7 }, { 3, 9 }, { 9, 12 }, { 11, 14 } };
范围列表结果=findRanges(目标范围、候选范围);
打印(结果);
返回0;
}
//递归函数,该函数返回候选多边形的最小子集
//覆盖给定的目标范围。
//如果没有覆盖targetRange的子集,则此函数
//返回一个空的范围列表。
//
范围列表搜索范围(范围目标范围、范围列表候选范围)
{
范围列表::迭代器;
到目前为止最小的列表;
for(it=候选人)
#include <utility>  // for std::pair
#include <vector>   // for std::vector
#include <iostream> // for std::cout & std::endl

typedef std::pair<int, int> range;
typedef std::vector<range> rangelist;

// function declarations
rangelist findRanges (range targetRange, rangelist candidateRanges);
void print (rangelist list);


int main()
{
    range target_range = { 3, 13 };

    rangelist candidate_ranges =
        { { 1, 4 }, { 30, 40 }, { 20, 91 }, { 8, 10 }, { 6, 7 }, { 3, 9 }, { 9, 12 }, { 11, 14 } };

    rangelist result = findRanges (target_range, candidate_ranges);

    print (result);
    return 0;
}


// Recursive function that returns the smallest subset of candidateRanges that
// covers the given targetRange.
// If there is no subset that covers the targetRange, then this function
// returns an empty rangelist.
//
rangelist findRanges (range targetRange, rangelist candidateRanges)
{
    rangelist::iterator it;
    rangelist smallest_list_so_far;

    for (it = candidateRanges.begin (); it != candidateRanges.end (); ++it) {

        // if this candidate range overlaps the beginning of the target range
        if (it->first <= targetRange.first && it->second >= targetRange.first) {

            // if this candidate range also overlaps the end of the target range
            if (it->second >= targetRange.second) {

                // done with this level - return a list of ranges consisting only of
                // this single candidate range
                return { *it };
            }
            else {
                // prepare new version of targetRange that excludes the subrange
                // overlapped by the present range
                range newTargetRange = { it->second + 1, targetRange.second };

                // prepare new version of candidateRanges that excludes the present range
                // from the list of ranges
                rangelist newCandidateRanges;
                rangelist::iterator it2;
                // copy all ranges up to but not including the present range
                for (it2 = candidateRanges.begin (); it2 != it; ++it2) {
                    newCandidateRanges.push_back (*it2);
                }
                // skip the present range
                it2++;
                // copy the remainder of ranges in the list
                for (; it2 != candidateRanges.end(); ++it2) {
                        newCandidateRanges.push_back (*it2);
                }

                // recursive call to find the smallest list of ranges that cover the remainder
                // of the target range not covered by the present range
                rangelist subList = findRanges (newTargetRange, newCandidateRanges);

                if (subList.size () == 0) {
                    // no solution includes the present range
                    continue;
                }
                else if (smallest_list_so_far.size () == 0 ||               // - first subList that covers the remainder of the target range
                         subList.size () < smallest_list_so_far.size ())    // - this subList is smaller than all previous ones checked
                {
                    // add the present range to the subList, which represents a solution
                    // (though possibly not optimal yet) at the present level of recursion
                    subList.push_back (*it);
                    smallest_list_so_far = subList;
                }
            }
        }
    }
    return smallest_list_so_far;
}

// print list of ranges
void print (rangelist list)
{
    rangelist::reverse_iterator rit;
    std::cout << "{ ";
    for (rit = list.rbegin (); rit != list.rend (); ++rit) {
        std::cout << "(" << rit->first << ", " << rit->second << ") ";
    }
    std::cout << "}" << std::endl;
}
(3, 9) (9, 12) (11, 14)