论"求最长重叠区间" 的两种方法

论 "求最长重叠区间" 的两种方法

问题描述

给定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;
}
*/
posted @ 2021-04-16 20:02  DeepJay  阅读(715)  评论(0编辑  收藏  举报