试题 算法训练 校门外的树
题目描述:
资源限制
时间限制: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 <stdlib.h>
#include <stdio.h>
#include <algorithm>
#include <iostream>
#include <string.h>
#include <math.h>
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 <stdlib.h>
#include <stdio.h>
#include <algorithm>
#include <iostream>
#include <string.h>
#include <math.h>
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;
}