Linux之centos安裝clinkhouse以及python如何連線
訊號
有 $n$ 個房子排成一排,從左到右依次編號為 $1 \sim n$。
其中一些房子內裝有無線訊號發射器。
這些訊號發射器的有效覆蓋半徑為 r。
更準確地說,如果第 $p$ 號房子內裝有訊號發射器,則所有房間編號在 $\left[ {p−r+1,p+r−1} \right]$ 範圍內的房子均可被其發出的無線訊號覆蓋,而其餘房子則不會被其發出的無線訊號覆蓋。
例如,假設 $n=6,r=2$,且第 $2$、$5$ 號房子內裝有訊號發射器,則第 $2$ 號房子內的發射器發出的訊號可以覆蓋第 $1 \sim 3$ 號房子,第 $5$ 號房子內的發射器發出的訊號可以覆蓋第 $4 \sim 6$ 號房子,將兩個發射器全部開啟,則無線訊號可以覆蓋到所有房子。
初始時,所有無線訊號發射器都是關閉的,請計算至少開啟多少個無線訊號發射器,才能保證所有房子都被無線訊號覆蓋到。
輸入格式
第一行包含兩個整數 $n$ 和 $r$。
第二行包含 $n$ 個整數 $a_{1},a_{2}, \dots,a_{n}$,每個數要麼是 $1$,要麼是 $0$,$a_{i}=1$ 表示第 $i$ 號房子內裝有無線訊號發射器,$a_{i}=0$ 表示第 $i$ 號房子內未裝無線訊號發射器。
輸出格式
一個整數,表示需要開啟的無線訊號發射器的最少數量。
如果無法使得所有房子都被無線訊號覆蓋到,則輸出 $−1$。
資料範圍
前 $6$ 個測試點滿足 $1 \leq n \leq 10$。
所有測試點滿足 $1\leq n,r \leq 1000$,$0 \leq a_{i} \leq 1$。
輸入樣例1:
6 2 0 1 1 0 0 1
輸出樣例1:
3
輸入樣例2:
5 3 1 0 0 0 1
輸出樣例2:
2
輸入樣例3:
5 10 0 0 0 0 0
輸出樣例3:
-1
輸入樣例4:
10 3 0 0 1 1 0 1 0 0 0 1
輸出樣例4:
3
解題思路
假設有兩個訊號發射器,一個比較靠前,一個比較靠後,並且兩個訊號發生器都可以覆蓋到最左邊的那個房子,即第$1$個房子。
我們考慮一下應該選擇哪個發生器。可以把所有的方案(最終答案的方案)分成兩類,第一類是選擇靠前的發生器的方案,第二類是選擇靠後的發生器的方案。我們任取第一類中的方案,且這個方案可以覆蓋所有的房子,我們把靠左的那個發生器換成靠右的發生器,仍然是一個合法的方案:
而且需要的發生器的數量是相同的,所以對於第一類中的方案,我們都可以在第二類中找到一個方案,使得這個方案所需要的發生器不比第一類的方案多。所以第二類中的方案都不比第一類的方案差,因此第二類方案中必然存在最優解(第一類方案也可能存在最優解),所以我們只需要考慮第二類的方案就可以了,即應該選擇靠後的那個發生器。
以此類推,在能夠覆蓋到最左邊那個還沒被覆蓋的房子的條件下,每次都應該選擇儘可能靠後的房子。
AC程式碼如下:
1 #include <cstdio> 2 #include <algorithm> 3 using namespace std; 4 5 const int N = 1010; 6 7 int a[N], sz; 8 9 int main() { 10 int n, m; 11 scanf("%d %d", &n, &m); 12 for (int i = 1; i <= n; i++) { 13 int val; 14 scanf("%d", &val); 15 if (val) a[sz++] = i; // 記錄有發生器的房間 16 } 17 18 int ret = 0, last = 0; // last表示上一個被覆蓋的房間,因此還沒被覆蓋的房間是last+1 19 for (int i = 0; i < sz; i++) { 20 if (last >= n) break; // 所有的房間已被覆蓋 21 // 第a[i]個房間最左邊能夠覆蓋到的房間為a[i]-m+1,如果a[i]-m+1 > last+1,表明不能夠覆蓋到上一個未覆蓋到的房間 22 if (a[i] - m > last) break; 23 24 int j = i; // 在能夠覆蓋last+1這個房間的前提下,尋找最靠右的房間 25 while (j + 1 < sz && a[j + 1] - m <= last) { 26 j++; 27 } 28 last = a[j] + m - 1; // 更新last 29 i = j; 30 ret++; 31 } 32 33 printf("%d", last >= n ? ret : -1); 34 35 return 0; 36 }
這題還可以用動態規劃來做。
AC程式碼如下:
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 using namespace std; 5 6 const int N = 1010; 7 8 int a[N], f[N]; 9 10 int main() { 11 int n, m; 12 scanf("%d %d", &n, &m); 13 for (int i = 1; i <= n; i++) { 14 scanf("%d", a + i); 15 } 16 17 memset(f, 0x3f, sizeof(f)); 18 for (int i = 1; i <= m; i++) { // 初始化一開始能夠覆蓋到第1個房間的發射器 19 if (a[i]) f[i] = 1; 20 } 21 for (int i = 1; i <= n; i++) { 22 if (a[i]) { 23 for (int j = max(1, i - 2 * m + 1); j < i; j++) { // 注意j可能會小於0越界 24 if (a[j]) f[i] = min(f[i], f[j] + 1); 25 } 26 } 27 } 28 29 int ret = N; 30 for (int i = max(1, n - m + 1); i <= n; i++) { // 列舉能夠覆蓋到第n個房間的最後一個發射器 31 if (a[i]) ret = min(ret, f[i]); 32 } 33 printf("%d", ret == N ? -1 : ret); 34 35 return 0; 36 }
參考資料
AcWing 4421. 訊號(AcWing杯 - 周賽):https://www.acwing.com/video/3871/