论 "求最长重叠区间" 的两种方法
问题描述
给定n个区间,第i个区间表示为([L_i,R_i]),求出区间重叠长度的最大值 ;
最大重叠长度 (Maxlength = max limits_{ 1leqslant j,k leqslant n , j eq k } { overlap( j , k ) }, overlap( j , k )) 表示 区间 j ,k的重叠部分的长度。
解法一:双指针(尺取法)
按区间左端点 L 的值对n个区间进行升序排序,对于区间 i ,向后依次遍历到区间 j ,
若 (R_j>R_i) , 则区间 i 所能贡献的最大重叠长度,不可能超过 (R_i-L_j) ,可删除区间 i ,并跳转到 j(i = j),
否则 区间 j 所能贡献的最大重叠长度,不可能超过(R_j-L_j) ,可删除区间 j ,继续向后遍历 (j = j+1);
每次遍历到一个区间,都会有一个区间被删除,因此求解的时间复杂度是O((n)) ,加之排序的复杂度O((nlog_n)) ,总复杂度为O((nlog_n))。
解法二:前缀最大值
维护一个前缀最大值 (ma_i=max(ma_{i-1},R_i)) ,
按区间左端点 L 的值对n个区间进行升序排序,根据贪心的思想,对于区间 i 查询 ,所有满足左端点 (lleqslant Li) 的区间,右端点 (r)的最大值 ,即 (ma_{i-1}); (因为排过序了,所以i 之前的所有区间都满足(leqslant L_i)) ,那么对于每一个区间 i ,我都通过前缀最大值向前O(1)查找了一个最大重叠长度 (length_i=min(ma_{i-1},Ri)-L_i) , (Maxlength=max limits_{1leqslant i leqslant n}length_i) 。
我这里只是向前查找,却没有向后查找,是因为区间的相交是双向关系,若 (Maxlength = overlap(i,j) ,i<j) , 虽然 i 查询范围里没有j,但是j的查询范围却有i。
这里的复杂度与解法一样,维护前缀最大值求解复杂度是O((n)) ,加之排序的复杂度O((nlog_n)) ,总复杂度为O((nlog_n))。
值得一提的是,第一种解法是向后查找,因此重叠区间的左端点为(L_j) ,而第二种解法是向前查找,因此重叠区间的左端点为(L_i) 。
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef unsigned long long uLL;
typedef pair<int,int> pii;
typedef pair<LL,LL> pLL;
typedef pair<double,double> pdd;
const int N=1e6+5;
const int M=2e5+5;
const int inf=0x3f3f3f3f;
const LL mod=998244353;
const double eps=1e-8;
const long double pi=acos(-1.0L);
#define ls (i<<1)
#define rs (i<<1|1)
#define fi first
#define se second
#define pb push_back
#define eb emplace_back
#define mk make_pair
#define mem(a,b) memset(a,b,sizeof(a))
LL read()
{
LL x=0,t=1;
char ch;
while(!isdigit(ch=getchar())) if(ch=='-') t=-1;
while(isdigit(ch)){ x=10*x+ch-'0'; ch=getchar(); }
return x*t;
}
pii a[N];
int main()//solution 1
{
int n=read();
for(int i=1;i<=n;i++) a[i].fi=read(),a[i].se=read();
sort(a+1,a+n+1);
int ans=0;
for(int i=1;i<n;)
{
int j=i+1;
while(j<=n)
{
ans=max(ans,min(a[i].se,a[j].se)-a[j].fi);
if(a[j].se>a[i].se) break;
j++;
}
i=j;
}
printf("%d
",ans);
return 0;
}
/*
solution 2
int main()
{
int n=read();
for(int i=1;i<=n;i++) a[i].fi=read(),a[i].se=read();
sort(a+1,a+n+1);
int ans=0,ma=0;
for(int i=1;i<=n;i++)
{
ans=max(ans,min(a[i].se,ma)-a[i].fi);
ma=max(ma,a[i].se);
}
printf("%d
",ans);
return 0;
}
*/