我的问题与algorithm to find longest non-overlapping sequences非常相似。
链接问题的唯一区别是,我不需要找到代表最长序列的非重叠元组集合,而是需要找到代表<的非重叠元组集合strong>最大覆盖率,我的意思是元组长度的总和是最大的(元组长度是last - first + 1
给定<的定义em> tuple 在下一句中。)
我代表我的元组与链接问题不同。而不是(starting index, length)
,我将我的元组表示为(first, last)
;例如,元组(3,7)
表示数字[3, 4, 5, 6, 7]
的集合。 (元组重叠另一个元组,即使端点匹配;即(2,6)
和(6,8)
重叠,因此不能同时出现在解决方案中。)
作为示例,请考虑以下{Q 1}组排序的元组:
first
此处的最大设置为[(0,100), (2,50), (30,150), (60,95), (110,190), (120,150), (191,200)]
,覆盖范围为[(0,100), (110,190), (191,200)]
。 (请注意,此解决方案中的元组不重叠。)
解决此问题的最简单算法的例子是什么,该算法的复杂性是什么? (如果能在101 + 81 + 10 = 192
中解决这个问题会很棒,但我现在还不知道它是否可以。)
ADDENDUM:回想起来,事实证明我在这里提出的问题等同于weighted interval scheduling problem。这是interval scheduling problem的一个特例。
@ j_random_hacker的答案,实际上是加权区间调度问题的已知解决方案,时间复杂度为O(N)
。
答案 0 :(得分:4)
这是 O(nlog n)-time,O(n)-space 算法。首先,如果元组数组尚未按此顺序排序,则按元组的起始位置排序。我将假设从零开始的数组索引。
让我们调用元组ib(i)的起始位置和结束位置e(i),使其总长度为e(i) - b(i)+ 1.同时让&#39; s定义一个函数next(i),它返回第一个元组的元组列表中可以出现在元组i右边的位置。注意next(i)可以用O(log n)时间用二进制搜索计算:只需将所有元组开始位置b(i)保存在数组b []中,并在子数组b中搜索第一个j [具有b [j]&gt;的i + 1 ... n-1]的E(i)。
让我们将f(i)定义为从开始或元组i后开始的任何非重叠元组的最大覆盖范围。由于元组本身或者处于这个最优集合中,我们有:
f(i) = max(e(i) - b(i) + 1 + f(next(i)), f(i+1)) for 0 <= i < n
我们也有边界条件f(n) = 0
。
显然,最大可能的覆盖范围由f(0)给出。这很容易计算出来。在伪C ++中:
int b[] = /* Tuple beginning positions, in nondecreasing order */;
int e[] = /* Tuple end positions */;
int n = /* Number of tuples */;
// Find the array position of the leftmost tuple that begins to the right of
// where tuple i ends.
int next(int i) {
return upper_bound(b + i + 1, b + n, e[i]);
}
int maxCov[n + 1]; // In practice you should dynamically allocate this
// After running this, maxCov[i] will contain the maximum coverage of any
// nonoverlapping subset of the set of n - i tuples whose beginning positions
// are given by b[i .. n-1] and whose ending points are given by e[i .. n-1].
// In particular, maxCov[0] will be the maximum coverage of the entire set.
void calc() {
maxCov[n] = 0;
for (int i = n - 1; i >= 0; --i) {
maxCov[i] = max(e[i] - b[i] + 1 + maxCov[next(i)], maxCov[i + 1]);
}
}
calc()
中的循环运行n次,每次迭代都会对二进制搜索函数upper_bound()
进行一次O(log n)调用。
我们可以通过计算f(0)的max()输入,查看哪一个实际产生最大值,记录它是否暗示元组0的存在与否,然后递归来重建这个大小的实际集合处理余数(对应于f(next(0))或f(1))。 (如果两个输入相等,则有多个最优解,我们可以跟随其中任何一个。)
答案 1 :(得分:2)
下面的算法通过递归检索每个元素是最左边成员的最大非重叠集合,然后返回覆盖范围最大的集合。请参阅代码中的注释。
在PHP中实现。您可以在此处http://viper-7.com/xowTRF
进行测试我认为此算法的复杂性为O(2^N)
或O(N^2)
缓存,如果您不同意,请随时发表评论。
$set = [[0,100], [2,50], [30,150], [60,95], [110,190], [120,150], [191,200]];
$GLOBALS['cache'] = array(); //cache for overlapping sub problems
function maximumSet($set) {
if(count($set) === 1) {
return $set;
}
$cache_key = [];
foreach($set as $k) {
$cache_key[] = implode($k,'.');
}
$cache_key = implode('_',$cache_key);
if(isset($GLOBALS['cache'][$cache_key])) {
return $GLOBALS['cache'][$cache_key];
}
$maximumResult = null;
//for each element in the set,
//get the largest non-overlapping set that the element is a member of
//once all N sets have been computed, return the largest one
foreach($set as $k => $element) {
unset($set[$k]);
//create a new set $copy, which contains the remaining elements that
//do not overlap with $element
$copy = $set;
foreach($set as $k2 => $element2) {
if($element2[0] <= $element[1]) {
//element is considered non overlapping if its start
//is less than or equal to current element's end
unset($copy[$k2]);
}
else break; //because the list is sorted we can break the 1st time
//see a non-overlapping element
}
if(!empty($copy)) {
//if there is at least one non-overlapping element
//recursively get the maximum set
$maximumSubset = maximumSet($copy);
//prepend the current element to it
array_unshift($maximumSubset,$element);
}
else {
//otherwise the maximum non-overlapping set which contains this element
//is the element itself
$maximumSubset = [$element];
}
//store the set in the results by coverage
$coverage = getCoverage($maximumSubset);
if(is_null($maximumResult) || $maximumResult['coverage'] < $coverage) {
$maximumResult = [
'coverage' => $coverage,
'set' => $maximumSubset
];
}
}
$GLOBALS['cache'][$cache_key] = $maximumResult['set'];
return $maximumResult['set'];
}
function getCoverage($set) {
$range = 0;
foreach($set as $v) {
$range += ($v[1] - $v[0]);
}
return $range;
}
$x = maximumSet($set);
print "<pre>";
print_r($x);
print "</pre>";