# Uva 10382: Watering Grass ###### tags: `貪心法` `區間覆蓋` > 一個長為 $l$, 寬為 $w$ 的田地有 $n$ 個灑水器, > 給定每個灑水器的位置, 以及其噴灑半徑 $r$, > 試計算出能噴到整個田地灑水器的最小數量, > 若無法完全覆蓋,則輸出 $-1$,反之輸出最小數量。 **Source:** [題目傳送門](https://vj.z180.cn/b569d6cab94c895b00989e543f81e077?v=1594628801) {%pdf https://vj.z180.cn/b569d6cab94c895b00989e543f81e077?v=1595085640 %} --- ## *Original* ### 題目敘述 > *$n$ sprinklers are installed in a horizontal strip of grass $l$ meters long and $w$ meters wide.* <br><br> > *Each sprinkler is installed at the horizontal center line of the strip.* <br><br> > *For each sprinkler we are given its position as the distance from the left end of the center line and its radius of operation.* ### Question > *What is the **minimim number** of sprinklers to turn on in order to **water the entire strip of grass?*** ![示意圖](https://i.imgur.com/0EeSyAB.png) ### Input format > *Input consists of a number of cases.* <br><br> > *The first line for each case contains integer numbers $n$, $l$ and $w$ with $n \leq 10000$.* <br><br> > *The next $n$ lines contain two integers giving the position of a sprinkler and its radius of operation. (The picture above illustrates the first case from the sample input.)* ### Output format > *For each test case output the **minimum** number of sprinklers needed to water the **entire** strip of grass. If it is **impossible** to water the entire strip output ‘-1’.* ### Sample Input 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 ### Sample Output 6 2 -1 --- ## 概念 * 貪心 * 區間覆蓋 --- ## 主要思路 * 首先,灑水器一律擺放至田地中央 $\frac{w}{2}$ 處,透過畢氏定理由 $\frac{w}{2}$ 與 $r$ 算出其可擴展到的左、右交點,如此便變成區間覆蓋問題 ![Figure 2](https://i.imgur.com/Qggm4fX.png) * 由二維題目轉變為單維,每個灑水器都有左、右可擴展到的交點位置,如上圖紅圈處標示,即代表在這區段之內的草坪皆可覆蓋到 * 接著依據左交點位置排序,而貪心在於每次查找時下一個必須涵蓋在當前的右交點內(其左交點必須在內),並儘可能的往右覆蓋 * 當 $r < \frac{w}{2}$ 時,該灑水器要捨棄掉(沒辦法完全覆蓋草坪的灑水器),此外若最左沒有覆蓋到0,或最右沒有覆蓋到 $l$,代表草坪無法覆蓋完全,則輸出 $-1$。 --- ## 程式碼 ```cpp= #include <iostream> #include <cmath> using namespace std; typedef struct S_pair { double left; double right; }s_pair; //qsort compare function int32_t compare(const void *a, const void *b) { if ((((s_pair *)a)->left) > (((s_pair *)b)->left)) return 1; return 0; } int main() { int32_t n = 0, l = 0, w = 0; while (cin >> n >> l >> w) { s_pair arr[n]; int32_t ans = 0; for (int32_t i = 0 ; i < n ; i ++) { int32_t pos = 0, r = 0; double len = 0; cin >> pos >> r; if (r < (double)(w/2)) { //失效的灑水器往不可能的地方擺即可 arr[i].left = l + 5000; arr[i].right= l + 5000; } else { len = sqrt(1.0 * r * r - (w/2.0)*(w/2.0)); arr[i].left = pos - len; arr[i].right= pos + len; } //若當個灑水器可覆蓋全部則ans=1 if (pos-len <= 0 && pos+len >= l) ans = 1; } if (ans) cout << "1" << endl; else { ans = 1; //依左交點排序 qsort(arr, n, sizeof(s_pair), compare); for (int32_t i = 0 ; i < n ; i++) { if (arr[i].right >= l) break; int32_t right_num = -1; for (int32_t j = i+1; j < n ; j++) { int32_t temp = (arr[i].right >= arr[j].left); if ((right_num == -1)&& temp ==1) { right_num = j; } else if (temp==1 && (arr[j].right > arr[right_num].right)) right_num = j; } i = right_num-1; ans +=1; if (right_num != -1 && arr[right_num].right >= l) break; //覆蓋失敗 if (right_num == -1) { ans = 0; break; } } //覆蓋成功 if (ans!=0 && arr[0].left <= 0) cout << ans << endl; else cout << "-1" << endl; } } return 0; }