论"求最长重叠区间" 的两种方法
论 "求最长重叠区间" 的两种方法
问题描述
给定n个区间,第i个区间表示为\([L_i,R_i]\),求出区间重叠长度的最大值 ;
最大重叠长度 \(Maxlength = \max \limits_{ 1\leqslant j,k \leqslant n , j \neq k } { overlap( j , k ) }, overlap( j , k )\) 表示 区间 j ,k的重叠部分的长度。
解法一:双指针(尺取法)
按区间左端点 L 的值对n个区间进行升序排序,对于区间 i ,向后依次遍历到区间 j ,
若 \(R_j>R_i\) ,\(i\)与\(j\)的重叠长度为\(R_i-L_j\),而\(L\)又是单调不递减的,因此区间\(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 查询 ,所有满足左端点 \(l\leqslant Li\) 的区间,右端点 \(r\)的最大值 ,即 \(ma_{i-1}\); (因为排过序了,所以i 之前的所有区间都满足\(\leqslant L_i\)) ,那么对于每一个区间 i ,我都通过前缀最大值向前O(1)查找了一个最大重叠长度 \(length_i=min(ma_{i-1},Ri)-L_i\) , \(Maxlength=\max \limits_{1\leqslant 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\n",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\n",ans);
return 0;
}
*/