# 2022/01/09 APCS 題解 + 心得&檢討 ###### tags: `資訊雙週計畫` 第1~2週 ![](https://i.imgur.com/8lmvAsn.png =730x160) ## 題解 ### ==第一題:程式交易 [題目連結](https://zerojudge.tw/ShowProblem?problemid=h081)== * 枚舉每天的股票價格,如果可以賣就賣,可以買就買。 * 複雜度:$O(n)$ :::spoiler code ```cpp= #include <bits/stdc++.h> using namespace std; #define FAST ios::sync_with_stdio(0), cin.tie(0); const int MAXN = 105; int n, d, ans; int a[MAXN]; int main() { FAST; cin >> n >> d; for (int i = 0; i < n; i++) cin >> a[i]; // now 紀錄現在的上一次的買(賣)的股票 // z 紀錄現在是否有股票 int now = a[0], have = 1; for (int i = 0; i < n; i++) { if (have == 1 && a[i] >= now+d) { // 可以賣出 ans += a[i]-now; // 利潤 now = a[i]; have = 0; } else if (have == 0 && a[i] <= now-d){ // 可以買進 now = a[i]; have = 1; } } cout << ans; return 0; } ``` ::: --- ### ==第二題:贏家預測 [題目連結](https://zerojudge.tw/ShowProblem?problemid=h082)== * 按照順序進行戰鬥,並更新每人戰鬥後的戰鬥力、應變力。下一次的戰鬥順序在對戰時記錄誰贏誰輸,並將贏的放入*win*,輸的放進*nnnwin*,完成所有戰鬥再將戰鬥順序更新。lose [ *i* ]紀錄 第*i*人輸了幾次,所以當 lose [ *i* ] *= m* 時,他將不能在戰鬥,總人數減一。當最終只剩下一人時,輸出答案。 * 複雜度:$O(nm)$ :::spoiler code ```cpp= #include <bits/stdc++.h> using namespace std; #define FAST ios::sync_with_stdio(0), cin.tie(0); using ll = long long; const ll MAXN = 1005; ll n, m; ll s[MAXN], t[MAXN], f[MAXN], lose[MAXN], len; ll win[MAXN], nnnwin[MAXN], w, l; void fight(ll i, ll j) { ll a = s[i], b = t[i]; ll c = s[j], d = t[j]; if (a*b >= c*d) { // i贏下比賽 s[i] = a+c*d/(2*b); t[i] = b+c*d/(2*a); s[j] = c+c/2; t[j] = d+d/2; if (++lose[j] == m) { // j輸的次數達到上限 win[w] = i; w++; n--; } else { win[w] = i; nnnwin[l] = j; w++; l++; } } else { // j贏下比賽 s[i] = a+a/2; t[i] = b+b/2; s[j] = c+a*b/(2*d); t[j] = d+a*b/(2*c); if (++lose[i] == m) { // i輸的次數達到上限 win[w] = j; w++; n--; } else { win[w] = j; nnnwin[l] = i; w++; l++; } } return ; } void solve() { while (n > 1) { len = n; // 戰鬥人數 w = 0, l = 0; for (int i = 1; i < len; i+=2) { // 兩兩戰鬥開始 fight(f[i-1], f[i]); } if (len%2) win[w] = f[len-1], w++; // 最後一人沒有人可以戰鬥算勝方 // 更新下一次戰鬥的順序 for (int i = 0; i < w; i++) f[i] = win[i]; for (int i = 0; i < l; i++) f[i+w] = nnnwin[i]; } cout << f[0]+1; return ; } int main() { FAST; cin >> n >> m; for (int i = 0; i < n; i++) cin >> s[i]; for (int i = 0; i < n; i++) cin >> t[i]; for (int i = 0; i < n; i++) cin >> f[i], f[i]--; solve(); return 0; } ``` ::: --- ### ==第三題:數位占卜 [題目連結](https://zerojudge.tw/ShowProblem?problemid=h083)== * 枚舉所有的字串,並且從字串中間位置+1開始枚舉作為第二個字串的開頭,如果兩字串前綴相等,就可以查詢是否有可以形成兩個相等的字串,查詢時用set的find來查找。 * 複雜度:$O(m\ len\ log(m))$ &rArr; $len$ 為字串長度![](https://i.imgur.com/uNFGMXK.png) :::spoiler code ```cpp= #include <bits/stdc++.h> using namespace std; #define FAST ios::sync_with_stdio(0), cin.tie(0); using ll = long long; const ll MAXN = 5e4+4; ll m, len, ans; string s[MAXN]; set<string> w; bool check(ll p, ll t, ll q) { // 判斷第一個和第二個字串前綴是否相同 for (int i = 0; i < q; i++) if (s[p][i] != s[p][i+t]) return 0; return 1; } void solve() { for (int i = 0; i < m; i++) { // 枚舉所有字串 len = s[i].size(); if (len == 1) continue; // 字串長度1無法分解 for (int j = len/2+len%2; j < len; j++) { // 判斷字串前綴是否相同,相同查詢是否有對應字串可以組成兩個相同字串 if ( check(i, j, len-j) && w.find( s[i].substr(len-j, 2*j-len ) ) != w.end() ) { ans++; } } } cout << ans; return ; } int main() { FAST; cin >> m; for (int i = 0; i < m; i++) cin >> s[i], w.insert(s[i]); solve(); return 0; } ``` ::: --- ### ==第四題:牆上海報 [題目連結](https://zerojudge.tw/ShowProblem?problemid=h084)== * 因為高度越低,連續的範圍越大,也就是可以掛更多海報;相對的高度越高,則掛的海報就較少。根據上述,因為符合單調性,因此可以對答案做2分搜,找出最大值。 * 複雜度:$O( (n+k)\ log(max(h)) )$ :::spoiler code ```cpp= #include <bits/stdc++.h> using namespace std; #define FAST ios::sync_with_stdio(0), cin.tie(0); #define eb emplace_back const int MAXN = 2e5+5; int n, k, mx; int h[MAXN], w[MAXN]; bool check(int x) { int now = 0; vector<int> v; for (int i = 0; i < n; i++) { if (h[i] >= x) { now++; } else if (now != 0) { v.eb(now); now = 0; } } if (now != 0) v.eb(now); int i = 0; for (int j = 0; i < k && j < v.size(); j++) { while (i < k && w[i] <= v[j]) { v[j] -= w[i]; i++; } } if (i >= k) return 1; else return 0; } void solve() { int L = 1, R = mx; // 值域 = [1,mx] // 對答案2分搜 while (L < R) { int mid = (L+R)>>1; if (L == mid) break; if (check(mid)) { // 判斷 高度mid 是否可以放置所有海報 L = mid; } else { R = mid-1; } } if (check(L+1)) cout << L+1; else cout << L; return ; } int main() { FAST; cin >> n >> k; for (int i = 0; i < n; i++) cin >> h[i], mx = max(mx, h[i]); for (int i = 0; i < k; i++) cin >> w[i]; solve(); return 0; } ``` ::: ## 心得 & 檢討 歷屆APCS成績: :::info | | 觀念題 | 實作題 | | -------- | -------- | -------- | | 2021/01/09 | 四級分(72) | 二級分(90) | | 2021/09/05 | 四級分(88) | 三級分(220)| | 2021/11/07 | 四級分(88) | 三級分(155)| | 2022/01/09 | **五級分(96)** | **三級分(220)** | ::: ### 觀念 觀念的部分我第一節考的時後寫太慢了,所以不小心猜了2題:cry:,雖然是有排除一些不可能的選項後才猜的,希望那題不算分或我猜對了。好險第二節我找回狀態,30分鐘就寫完了,所以就在檢查一次,結果發現有一題我知道答案是B卻寫成C,真的是好險有檢查喔。 總之,希望這次觀念會有5級分,我不想要再拿88分(4級分頂):angry:。 ### 實作 這次的實作真的是超級簡單:scream:,考試時的想法都是正確的,Zerojudge的題目出來後就立刻測試,果然全部都AC,但是可惜這不是在考試當下。 考試當下,pA一下子就秒殺掉,接下來想說先去解個算法的題目,直接跳pC,雖然想到了我上面題解的想法,但是我不太相信自己,以為複雜度會爛掉,所以寫個20分暴力解就跳回pB了:cry:。pB很顯然的比上次簡單許多,但是自己應該是太少寫這種很麻煩的題目,所以bug就一大堆,寫完後,至少花了1個半小時debug吧?:tired_face:到最後剩10分鐘時總算把所有的bug清掉,然後決定去看看pD長什麼樣子,沒想到他居然比pC還要簡單很多,就直接對答案二分搜就可以了。要是先看到pD再看 pB pC,我這次就穩實作四級分了qq。所以這次預估實作只有三級分(220分)。 總之,這次就算是學了個教訓吧!有實力拿「實作5」的我這次可能只有3級分,這兩個分數真的差很多啊,沒辦法把握這麼好的一次機會真的可惜。下次還是乖乖的把所有題目看完,排好難度後再寫。 特殊選才前,還有6月和10月的兩次的APCS,希望能夠好好把握6月的APCS,因為10月考完後,成績公布時應該有些學校都已經面試完了。所以希望6月的APCS能夠盡量達到<font color="#f00">**觀念5實作4以上**</font>的成績。加油!!!:muscle::muscle::muscle: (題外話:寫完這篇剛好是我生日ㄝ01/12:laughing:,17歲快樂~) ## 成績公布日~ 終於公布了!雖然說這次實作很可惜,但是好險,觀念題有96,五級分,算是沒有白考了這次APCS。希望下次六月能夠不要燒雞,至少拿到實作四級,拚五級!加油! ![](https://i.imgur.com/lC2Tm35.png =700x360)