试题 算法训练 校门外的树(贪心法)---蓝桥杯

试题 算法训练 校门外的树

题目描述:

资源限制
时间限制:1.0s 内存限制:256.0MB
问题描述
  某校大门外长度为L的马路上有一排树,每两棵相邻的树之间的间隔都是1米。我们可以把马路看成一个数轴,马路的一端在数轴0的位置,另一端在L的位置;数 轴上的每个整数点,即0,1,2,……,L,都种有一棵树。
  由于马路上有一些区域要用来建地铁。这些区域用它们在数轴上的起始点和终止点表示。已 知任一区域的起始点和终止点的坐标都是整数,区域之间可能有重合的部分。现在要把这些区域中的树(包括区域端点处的两棵树)移走。你的任务是计算将这些树 都移走后,马路上还有多少棵树。
输入格式
  输入文件的第一行有两个整数L(1 <= L <= 10000)和 M(1 <= M <= 100),L代表马路的长度,M代表区域的数目,L和M之间用一个空格隔开。接下来的M行每行包含两个不同的整数,用一个空格隔开,表示一个区域的起始点 和终止点的坐标。
输出格式
  输出文件包括一行,这一行只包含一个整数,表示马路上剩余的树的数目。
样例输入
500 3
150 300
100 200
470 471
样例输出
298
数据规模和约定
  对于20%的数据,区域之间没有重合的部分;
  对于其它的数据,区域之间有重合的情况。
  
这道题的解答有两种思路:

(1)贪心,找到覆盖区间,就是需要剔除掉的树(注意交叉区间重复的树),剩下的就是马路上留下的树的个数啦(2)由于是离散型的,可以使用一维数组来存储,将需要砍掉树的区间的数组值为1,那么数组值为0的就是不应该砍掉的树啦。

(个人对于这个题推荐第二种解法,当然仅仅是对于这个题来说,贪心是大家必须要懂的算法)

解题思路一(贪心法):

按照左端点递增排序,排除掉可以被覆盖的区间,选择起点在start的最长区间【ai,bi】,选择新的起点start(有两种情况,如果ai+1>bi,更新start为ai+1,否则更新start为bi),之后忽略所有区间在bi之前的部分。

注意(一定要看):

(1)排序后剔除掉可以被覆盖的区间后,第一个区间一定是符合答案的区间的一个部分。不知道的可以简单的推理一下即可。
(2)最长区间指的是bi-start最大的值的区间。

AC代码:

#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
struct Node
{
	int l;//区间的左端点 
	int r;//区间的右端点
	int flag;//是否访问过 
}a[100],b[100];
bool cmp(Node x,Node y)
{
	if(x.l==y.l)
		return x.r>y.r;
	else
		return x.l<y.l;
}
int main()
{	
	int n,m,i,j;//n代表马路的长度,m代表区间的个数 
	cin>>n>>m;
	for(i=0;i<m;i++)
	{
		cin>>a[i].l>>a[i].r;
		a[i].flag=0;
	}
	sort(a,a+m,cmp);
	
	for(i=0;i<m;i++)
		for(j=i+1;j<m;j++)
		{
			if(a[j].r<=a[i].r&&!a[j].flag)//第j个区间被覆盖了 ,要剔除 
				a[j].flag=1;
		}
	int k=0;
	for(i=0;i<m;i++)//剔除掉被包含的区间(可以被完全覆盖的区间 ) 
		if(!a[i].flag)//第i个区间被覆盖了 ,要剔除 
		{
			b[k].l=a[i].l;
			b[k].r=a[i].r;//将a结构体中剔除后的数据拷贝给结构体b,方便后续计算 
			k++;
		}
	//此时的b中的所有区间不存在覆盖的情况 (其中第一个区间一定是符合结果的区间) 
	i=0;
	int ans=0;
	int start=a[0].l;//每次定的所需区间的起点 
	int cnt=0,qq;
	while(i<k)
	{//i+1==m是因为扫描到倒数第一个,后面没有区间啦,没有办法通过b[i+1].l>start这个条件 
		if(b[i].l<=start&&(b[i+1].l>start||i+1==k))//符合条件 
		{
			qq=(b[i].r-start+1);//qq为每次所需要计算的数的个数(先计算,重复的后面再处理) 
			ans+=qq;
			start=b[i].r;
			if(b[i+1].l>start)
			{
				start=b[i+1].l;
				cnt++;//cnt为根本不会重叠的区间 
			}
		}
		i++;
	}
	int repeat=k-cnt-1;//repeat为重复减去的树的个数(1为第一个区间,肯定符合条件的) 
	cout<<(n+1)-ans+repeat<<endl;
	return 0;
}

解题思路二(数组存储):(推荐这个方法,很简单)

思路上面已经说过,由于这个很简单不细说。

AC代码:

#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
int a[10000];
int main()
{	
	int n,m,x,y;
	cin>>n>>m;
	int i,j;
	memset(a,0,sizeof(a));
	for(i=0;i<m;i++)
	{
		cin>>x>>y;
		for(j=x;j<=y;j++)
			a[j]=1;
	}	
	int ans=0;
	for(i=0;i<=n;i++)
		if(!a[i]) 
			ans++;
	cout<<ans<<endl;
	return 0;
}

你可能感兴趣的:(蓝桥,贪心)