試題 算法訓練 校門外的樹(貪心法)---藍橋杯

試題 算法訓練 校門外的樹

題目描述:

資源限制
時間限制: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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章