1. 程式人生 > 其它 >選元素

選元素

選元素

給定一個長度為 $n$ 的整數序列 $a_{1},a_{2}, \dots ,a_{n}$。

請你從中挑選 $x$ 個元素,要求:

  1. 原序列中的每一個長度為 $k$ 的連續子序列都至少包含一個被選中的元素。
  2. 滿足條件 $1$ 的前提下,所選 $x$ 個元素的相加之和應儘可能大。

輸出最大可能和。

輸入格式

第一行包含三個整數 $n,k,x$。

第二行包含 $n$ 個整數 $a_{1},a_{2}, \dots,a_{n}$。

輸出格式

如果無法滿足題目要求,則輸出 $−1$。

否則,輸出一個整數,表示所選元素的最大可能和。

資料範圍

前三個測試點滿足 $1 \leq k,x \leq n \leq 6$。
所有測試點滿足 $1 \leq k,x \leq n \leq 200,1 \leq a_{i} \leq {10}^{9}$。

輸入樣例1:

5 2 3
5 1 3 10 1

輸出樣例1:

18

輸入樣例2:

6 1 5
10 30 30 70 10 10

輸出樣例2:

-1

輸入樣例3:

4 3 1
1 100 1 1

輸出樣例3:

100

解題思路

  首先可以發現這題的選法有很多,大概是指數級別的。例如,如果$k = n$,那麼我們的選法就有$C_{n}^{x}$種,那麼這題大概率用動態規劃或者貪心來做。

  最後的答案應該是$max\left\{f \left( {n, x} \right), f \left( {n-1, x} \right), \dots, f \left( {n-k+1, x} \right) \right\}$,因為最後一個數$n$不一定會被選,而且因為要求長度為$k$的子序列中至少要有$1$個數被選,因此最遠可以選到第$i-k+1$個數。

  時間複雜度為$O \left( n^{3} \right)$,AC程式碼如下:

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 using namespace std;
 5 
 6 typedef long long LL;
 7 
 8 const int N = 210;
 9 
10 LL a[N], f[N][N];
11 
12 int main() {
13     int n, k, m;
14     scanf("%d %d %d", &n, &k, &m);
15 for (int i = 1; i <= n; i++) { 16 scanf("%d", a + i); 17 } 18 19 // 處理邊界 20 memset(f, -0x3f, sizeof(f)); 21 // 這裡處理f(i, 1)的情況,也可以直接f(0, 0) = 0 22 for (int i = 1; i <= k; i++) { 23 f[i][1] = a[i]; 24 } 25 26 for (int i = 1; i <= n; i++) { 27 for (int j = 1; j <= m; j++) { 28 for (int u = max(1, i - k); u < i; u++) { 29 f[i][j] = max(f[i][j], f[u][j - 1] + a[i]); 30 } 31 } 32 } 33 34 LL ret = -1; 35 for (int i = n; i > n - k; i--) { 36 ret = max(ret, f[i][m]); 37 } 38 printf("%lld", ret); 39 40 return 0; 41 }

  在進行狀態計算的時候,可以發現對於固定的$j$,每次都是從一個長度為$k$的視窗中求一個最大值,即$i$列舉的範圍是$i-k \sim i-1$。因此可以用單調佇列進行優化,先列舉$j$,再列舉$i$,每次維護長度為$k$的視窗中的最大值。

  時間複雜度為$O \left( n^{2} \right)$,AC程式碼如下:

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 using namespace std;
 5 
 6 typedef long long LL;
 7 
 8 const int N = 210;
 9 
10 LL a[N], f[N][N];
11 LL q[N], hh, tt = -1;
12 
13 int main() {
14     int n, k, m;
15     scanf("%d %d %d", &n, &k, &m);
16     for (int i = 1; i <= n; i++) {
17         scanf("%d", a + i);
18     }
19     
20     memset(f, -0x3f, sizeof(f));
21     for (int i = 1; i <= k; i++) {
22         f[i][1] = a[i];
23     }
24     
25     for (int j = 2; j <= m; j++) {
26         hh = 0, tt = -1;
27         for (int i = 1; i <= n; i++) {
28             if (hh <= tt && q[hh] < i - k) hh++;
29             if (hh <= tt) f[i][j] = f[q[hh]][j - 1] + a[i];
30             while (hh <= tt && f[q[tt]][j - 1] <= f[i][j - 1]) {
31                 tt--;
32             }
33             q[++tt] = i;
34         }
35     }
36     
37     LL ret = -1;
38     for (int i = n; i > n - k; i--) {
39         ret = max(ret, f[i][m]);
40     }
41     printf("%lld", ret);
42     
43     return 0;
44 }

參考資料

  AcWing 4418. 選元素(AcWing杯 - 周賽):https://www.acwing.com/video/3859/