校门外的树 知识点:区间合并算法

70 阅读2分钟

422. 校门外的树 - AcWing题库

朴素做法

刚开始将数组初始化为1,然后进行m个区间的删除操作,即把m个区间标记为0,最后把还剩的所有的1全部相加就是最后剩的个数。

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
int st[N];
int main()
{
	int l,m;cin>>l>>m;
	
	//题目描述是从0~L,一共L+1棵树 
	for(int i=0;i<=l;i++)st[i]=1;  
	
	while(m--)
	{
	  int a,b;cin>>a>>b;
	  //删除区间
	  for(int i=a;i<=b;i++)
	  {
	     st[i]=0;	
	  }	
	}
	
	//看一下还剩多少个数
	int res=0;
	for(int i=0;i<=l;i++)
	{
	  res+=st[i];	
	} 
	
	cout<<res<<endl;
	return 0;
}

时间复杂度:一共m个区间,每个区间最大长度是L,一共是O(lm),lm最大1e6。

优化做法: 合并区间算法

我们把所有要删除的区间进行合并,并把每个区间长度相加,最后用总长度L减去合并之后的区间的长度,就是还剩下的数的个数:

#include<bits/stdc++.h>
using namespace std;
#define l first
#define r second 
typedef pair<int,int>PII;  
const int N=110;
PII v[N];
int main()
{
	int len,m;cin>>len>>m;
    
	//输入m个区间	
	for(int i=0;i<m;i++)
    {
    	cin>>v[i].l>>v[i].r;
	}
	
	sort(v,v+m); //PII默认按照左端点排序 
	
	//开始合并 
    int sum=0;
	int L=v[0].l,R=v[0].r;
	for(int i=1;i<m;i++) 
	{
		if(v[i].l<=R)R=max(R,v[i].r);  //如果当前区间的左端点小于前一段区间的右端点,就合并 
	    else  //不相交,每一段区间都是独立的   
	    { 
	    	sum+=R-L+1; //加上前一段区间的区间和 
		    L=v[i].l,R=v[i].r; //更新前一段区间为当前区间 
		}
	}
	sum+=R-L+1;  //加上最后一段区间 
    cout<<len+1-sum<<endl;  //还剩多少区间=总区间-合并之后的区间 

	return 0;
}