算法 {区间最大覆盖(让一个固定长度区间来覆盖若干区间)}
区间最大覆盖(让一个固定长度区间来覆盖若干区间)
定義
一維坐標系上 有若干個不相交(端點可以重合)的區間A, 比如按照左端點排序後為[l1,r1], [l2,r2], ...
滿足r1<=l2
, 選擇一個長度為Len
的區間 使得他與A的交集的長度最大;
.
比如A= [ [0,1], [2,4], [8,9]]; Len=4
, 選擇答案區間為[0,4]
交集長度為3;
假如區間集合A 他裡面的區間是相交的, 那麼你可以先進行一遍區間合併(即把[0,4] [2,5]
合併為[0,5]
);
算法
模板
令答案區間為[L,R]
, 可以證明 他一定可以形如: R=ri
或 L=li
的性質, 因此使用雙指針算法;
下面的代碼有2個 (一個是計算R=ri
)(另一個是計算L=li
), 求出的結果是一樣的;
代碼
{ // ___Intervals_MaximumCoverage (枚舉答案區間的右端點)
using __PointType_ = ?;
const pair<__PointType_,__PointType_> * __Itvs = ?.data();
ASSERT_MSG_( "__Itvs是按照*左端點*排序的,且不會相交(比如`[[1,2] [2,3] [4,5]]`是合法的)");
using __AnsType_ = conditional_t< is_integral<__PointType_>::value, long long, Double>;
const int __N = ?;
const __AnsType_ __MaxLen = ?;
int __lefInd = 0;
__AnsType_ __ANS = 0;
for( int i = 0; i < __N; ++i){
__AnsType_ lefPos = __Itvs[ i].second - __MaxLen;
while( __lefInd < i && lefPos >= __Itvs[ __lefInd].second){
__ANS -= (__Itvs[ __lefInd].second - max<ANS_TYPE_>( __Itvs[i-1].second - __MaxLen, __Itvs[ __lefInd].first));
++ __lefInd;
}
if( __lefInd != i){
__ANS -= (__Itvs[ __lefInd].second - max<ANS_TYPE_>( __Itvs[i-1].second - __MaxLen, __Itvs[ __lefInd].first));
__ANS += (__Itvs[ __lefInd].second - max<ANS_TYPE_>( lefPos, __Itvs[ __lefInd].first));
__ANS += (__Itvs[ i].second - __Itvs[ i].first);
}
else{
__ANS = (__Itvs[ i].second - max<ANS_TYPE_>( lefPos, __Itvs[ i].first));
}
//>< 如果答案區間為`[lefPos, Itvs[i].second]` 此時他的覆蓋長度為`__ANS`;
}
} // ___Intervals_MaximumCoverage (枚舉答案區間的右端點)
{ // ___Intervals_MaximumCoverage (枚舉答案區間的左端點)
using __PointType_ = ?;
const pair<__PointType_,__PointType_> * __Itvs = ?.data();
ASSERT_MSG_( "__Itvs是按照*左端點*排序的,且不會相交(比如`[[1,2] [2,3] [4,5]]`是合法的)");
using __AnsType_ = conditional_t< is_integral<__PointType_>::value, long long, Double>;
const int __N = ?;
const __AnsType_ __MaxLen = ?;
int __rigInd = __N-1;
__AnsType_ __ANS = 0;
for( int i = __N-1; i >= 0; --i){
ANS_TYPE_ rigPos = __Itvs[ i].first + __MaxLen;
while( __rigInd > i && rigPos <= __Itvs[ __rigInd].first){
__ANS -= (min<ANS_TYPE_>( __Itvs[i+1].first + __MaxLen, __Itvs[ __rigInd].second) - __Itvs[ __rigInd].first);
-- __rigInd;
}
if( __rigInd != i){
__ANS -= (min<ANS_TYPE_>( __Itvs[i+1].first + __MaxLen,__Itvs[ __rigInd].second) - __Itvs[ __rigInd].first);
__ANS += (min<ANS_TYPE_>( rigPos, __Itvs[ __rigInd].second) - __Itvs[ __rigInd].first);
__ANS += (__Itvs[ i].second - __Itvs[ i].first);
}
else{
__ANS = (min<ANS_TYPE_>( rigPos, __Itvs[ i].second) - __Itvs[ i].first);
}
//>< 如果答案區間為`[Itvs[i].first, rigPos]` 此時他的覆蓋長度為`__ANS`;
}
} // ___Intervals_MaximumCoverage (枚舉答案區間的左端點)
2個答案區間
用兩個長度為Len
的區間去覆蓋 求最大覆蓋長度;
此時答案分為2種情況:
1(這2個答案區間是連續的): 即[L, L+Len] [L+Len, L+2*Len]
, 此時就轉換為一個2*Len
區間覆蓋問題;
2(不是連續的): 即[Li, Li+Len] [Lj, Lj+Len]
此時令Lef[i] = max(R1,R2,...,Ri)
其中Ri
表示(選擇一個長度為Len
且右端點與ri
重合的最大覆蓋長度), 同理Rig[i] = max(Li,L[i+1],...,LN)
Li
表示(左端點為li
的長度為Len
的答案區間的最大覆蓋), 然後枚舉max( Lef[i] + Rig[i+1])
為答案;