【C++】競程筆記(枚舉習題練習)

  1. https://zerojudge.tw/ShowProblem?problemid=c435
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <bits/stdc++.h>
using namespace std;

int main() {
ios_base::sync_with_stdio(false);
cin.tie(NULL);

int n;
cin >> n;
vector<int> a(n);
for (int i = 0; i < n; i++) {
cin >> a[i];
}

int max_left = a[0];
int ans = INT_MIN; // INT_MIN 是 int 資料型態的最小值 -2^31
for (int j = 1; j < n; j++) {
ans = max(ans, max_left - a[j]);
max_left = max(max_left, a[j]);
}

cout << ans << endl;
return 0;
}

找最大差值。 a[i]a[j]a[i] - a[j] ,但 i<ji<j

解題思路:定義一個變數 max_left = a[0],然後用他去紀錄當前位置 jj 左邊的最大值(即 a[0]a[0]a[j1]a[j-1] 的最大值),而對於每個 jj ,計算 max_left - a[j] 的最大值。

而變數 ansans 則是用來記錄所有計算出的 max_left - a[j] 的最大值。

處理完當前的 jj 後,將 max_left 指定為 max(max_left, a[j]),以繼續用於下一個位置 j+1j+1

最後的時間複雜度為 O(n)O(n)

大致上有三種情況要去考慮:

遞減數列: {5,4,3,2,1}\{5,4,3,2,1\} ,最大差值就是 a[0]a[4]=4a[0] - a[4] = 4

遞增數列: {1,2,3,4,5}\{1,2,3,4,5\}

  • j = 1, 1 - 2 = -1, ans = -1, max_left = 2
  • j = 4, 4 - 5 = -1, ans = -1
  • 最終 ans = -1, 所以最大差值就 -1。

相同元素的數列: {10000,10000,10000}\{10000, 10000, 10000\}

  • j = 1, 10000 - 10000 = 0, ans = 0
  • j = 2, 10000 - 10000 = 0, ans = 0
  • 最終 ans = 0, 所以最大差值 = 0。
  1. https://codeforces.com/problemset/problem/1352/E
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include <bits/stdc++.h>
using namespace std;

int main() {
// Optimize I/O
ios::sync_with_stdio(false);
cin.tie(0);

int t;
cin >> t;
for (int test = 0; test < t; test++) {
int n;
cin >> n;

vector<int> a(n);
for (int i = 0; i < n; i++) {
cin >> a[i];
}

// Compute prefix sums
vector<long long> prefix(n + 1, 0); // {0, 0, ..., n+1 numbers 0}
for (int i = 1; i <= n; i++) {
prefix[i] = prefix[i - 1] + a[i - 1];
}

vector<bool> achievable(n + 1, false);
for (int i = 0; i < n - 1; i++) {
for (int j = i + 1; j < n; j++) {
long long sum = prefix[j + 1] - prefix[i];
if (sum > n) break; // No need to check larger sums
achievable[sum] = true;
}
}

// Count special elements
int count = 0;
for (int i = 0; i < n; i++) {
if (achievable[a[i]]) { // a[i] ≤ n by problem constraint
count++;
}
}

cout << count << "\n";
}

return 0;
}

題目大意:

給定一個序列 a1,a2,,ana_{1}, a_{2}, \cdots , a_{n} ,若有一個長度至少為 22 的區間總和 al+al+1++ar=aia_{l} + a_{l+1} + \cdots + a_{r} = a_{i} ,則 aia_{i} 是 special element。求序列內有幾個 special elements。

解題思路:

首先可能會想到要直接枚舉所有可能的子陣列(從索引 iijj,其中 ji+1j \geq i+1 )並計算其和,時間複雜度為 O(n3)O(n^{3}) (枚舉起點 O(n)O(n) 、終點 O(n)O(n) 、計算和 O(n)O(n) ),因為題目限制 n8000n \leq 8000 ,這樣會太慢。

所以可以使用 Prefix sum,前綴和去求區間和,那個 sum 就是在用前綴和求區間和了。

判斷式 if (sum > n) break; 是優化算法,如果總和加一加超過陣列長度的話,那基本上可以不用再檢查了,就直接 break。

最後是看 achievable 的布林值情況,如果 true 就計數,這邊 O(n)O(n)

備註:後面的題目不知道為什麼 NTUCPC Guide 的連結打不開,所以就先這樣。喔對了,OwO 學長那題我解不出來,所以放棄 orz,link:https://zerojudge.tw/ShowProblem?problemid=c412