試題 算法訓練 校門外的樹
題目描述:
資源限制
時間限制: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;
}