1. 程式人生 > 其它 >Linux之centos安裝clinkhouse以及python如何連線

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/