LOJ#10002 噴水裝置(貪心)
阿新 • • 發佈:2019-01-05
題目描述
長 L米,寬 W 米的草坪裡裝有 n 個澆灌噴頭。每個噴頭都裝在草坪中心線上(離兩邊各 W/2 米)。我們知道每個噴頭的位置(離草坪中心線左端的距離),以及它能覆蓋到的澆灌範圍。
請問:如果要同時澆灌整塊草坪,最少需要開啟多少個噴頭?
輸入格式
輸入包含若干組測試資料。
第一行一個整數 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; } }