1. 程式人生 > >LOJ#10002 噴水裝置(貪心)

LOJ#10002 噴水裝置(貪心)

題目連結

題目描述

長 L米,寬 W 米的草坪裡裝有 n 個澆灌噴頭。每個噴頭都裝在草坪中心線上(離兩邊各 W/2​​ 米)。我們知道每個噴頭的位置(離草坪中心線左端的距離),以及它能覆蓋到的澆灌範圍。

請問:如果要同時澆灌整塊草坪,最少需要開啟多少個噴頭?

Picture1

輸入格式

輸入包含若干組測試資料。

第一行一個整數 T 表示資料組數;

每組資料的第一行是整數 n、L 和 W;

接下來的 n 行,每行包含兩個整數,給出一個噴頭的位置和澆灌半徑(上面的示意圖是樣例輸入第一組資料所描述的情況)。

輸出格式

對每組測試資料輸出一個數字,表示要澆灌整塊草坪所需噴頭數目的最小值。如果所有噴頭都開啟也不能澆灌整塊草坪,則輸出 −1 。

樣例

樣例輸入

3
8 20 2
5 3
4 1
1 2
7 2
10 2
13 3
16 2
19 4
3 10 1
3 5
9 3
6 1
3 10 1
5 3
1 1
9 1

樣例輸出

6
2
-1

資料範圍與提示

對於 100% 的資料,n≤15000。

將噴頭噴水的有效範圍求出來並過濾掉噴水直徑小於草坪寬的噴頭。

然後可以將問題轉化為區間完全覆蓋問題。等價於給定一個長度為m的區間,再給出n條線段的起點和終點(注意這裡是閉區間),求最少使用多少條線段可以將整個區間完全覆蓋。

將每一個區間按照左端點遞增順序排列,這裡貪心的策略是每次在可選線段裡選取右端點最大的,直至完全覆蓋。

AC程式碼:

#include<iostream>
#include<algorithm>
#include<cstring>
#include<iomanip>
#include<cmath>
#include<vector>
#include<stack>
using namespace std;
int main()
{
	int T;cin>>T;
	//Sprinkler head,用first儲存左端點,用second儲存右端點 
	vector< pair<double,double> > sh;
	pair<double,double> p;
	while(T--) 
	{
		int n,L;double W;
		cin>>n>>L>>W;
		sh.clear();
		for(int i=1;i<=n;i++)
		{
                    int x;double r; 
		    cin>>x>>r;
		    if(r<W/2) continue;
		    r=sqrt(r*r-(W/2)*(W/2));
		    p=make_pair(x-r,x+r);
		    sh.push_back(p);
		}
		sort(sh.begin(),sh.end());//左端點非遞減,若左端相同則右端點非遞減(其實右端點怎樣排序無所謂) 
		
		int i=0,ans=0;
		double t=0;//t是已灌溉區域的右端點 
		bool flag=1;
		while(i<sh.size())
		{   
			if(t<sh[i].first) {flag=0;break;}//中間有一段不能完全覆蓋 
			if(t>=L) break;//已經完全覆蓋 
			double max_r=-1;
			while(i<sh.size()&&t>=sh[i].first)//在可選範圍中選取右端點最大的 
			{
				max_r=max(max_r,sh[i].second);
				i++;
			}
			t=max_r;
			ans++;
		}
		if(flag) cout<<ans<<endl;
		else cout<<"-1"<<endl;
	}
}