[英]Algorithm to find maximum coverage of non-overlapping sequences. (I.e., the Weighted Interval Scheduling Prob.)
我有一個與算法非常相似的問題, 以找到最長的非重疊序列 。
與鏈接問題的唯一區別在於,我不需要找到代表最長序列的非重疊元組集合,而是需要找到代表最大覆蓋范圍的非重疊元組集合,我的意思是表示最大覆蓋的總和。元組長度是最大的( 元組長度是last - first + 1
給定下一個句子中元組的定義)。
我代表我的元組與鏈接問題不同。 而不是(starting index, length)
,我將我的元組表示為(first, last)
; 例如,元組(3,7)
代表一組數字[3, 4, 5, 6, 7]
(3,7)
[3, 4, 5, 6, 7]
。 (即使端點匹配,元組也會重疊另一個元組;即, (2,6)
和(6,8)
重疊 ,因此不能同時出現在解決方案中。)
例如,考慮以下一組元組,按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
。 (請注意,此解決方案中的元組不重疊 。)
什么是解決這個問題的最簡單算法的例子,該算法的復雜性是什么? (如果可以在O(N)
解決這個問題會很棒,但我現在還不知道它是否可以。)
附錄:回想起來,事實證明我在這里提出的問題等同於加權區間調度問題 。 這是區間調度問題的一個特例。
事實上,@ j_random_hacker的答案是加權區間調度問題的已知解決方案,其時間復雜度為O(N log(N))
。
這是一個O(nlog n)時間,O(n)空間算法。 首先,如果元組數組尚未按此順序排序,則按元組的起始位置排序。 我將假設從零開始的數組索引。
讓我們調用元組ib(i)的起始位置和結束位置e(i),使其總長度為e(i) - b(i)+ 1.還讓我們定義一個函數next(i)返回第一個元組的元組列表中的位置,可以出現在元組i的右側。 注意next(i)可以用O(log n)時間用二進制搜索計算:只需將所有元組開始位置b(i)保存在數組b []中,並在子數組b中搜索第一個j [ i + 1 .. n-1]具有b [j]> 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))。 (如果兩個輸入相等,那么有多個最優解,我們可以跟隨其中任何一個。)
下面的算法通過遞歸檢索每個元素是最左邊成員的最大非重疊集合,然后返回具有最大覆蓋率的集合。 請參閱代碼中的注釋。
用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>";
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.