簡體   English   中英

找到非重疊序列的最大覆蓋范圍的算法。 (即,加權區間調度問題。)

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM