# APCS 2025/01
## [第一題 等紅綠燈](https://zerojudge.tw/ShowProblem?problemid=q181)
a + b是紅綠燈的一個週期,所以x % (a + b)是那個循環的第?秒,所以判斷他是在綠燈的時候或是在等紅燈時。
```cpp=
#include <bits/stdc++.h>
using namespace std;
int main() {
int a, b;
cin >> a >> b;
int t, ans = 0;
cin >> t;
while (t--) {
int x;
cin >> x;
x %= (a + b);
if (x >= a) ans += a + b - x;
}
cout << ans;
}
```
## [第二題 字串操作](https://zerojudge.tw/ShowProblem?problemid=q182)
這題很簡單,照著題目敘述去做就好。
```cpp=
#include <bits/stdc++.h>
using namespace std;
int main() {
string s;
cin >> s;
int t;
cin >> t;
int n = s.size();
while (t--) {
int x;
cin >> x;
if (x == 0) {
for (int i = 1;i < n;i += 2) swap(s[i], s[i - 1]);
}
if (x == 1) {
for (int i = 1;i < n;i += 2) {
if (s[i] < s[i - 1]) swap(s[i], s[i - 1]);
}
}
if (x == 2) {
string a = s;
for (int i = 0, j = 1;j < n;i++, j += 2) {
s[j - 1] = a[i];
s[j] = a[i + n / 2];
}
}
}
cout << s;
}
```
## [第三題 重組問題](https://zerojudge.tw/ShowProblem?problemid=q183)
先記錄每個距離出現的次數,然後利用 dfs 函式找出可能的數列組合。dfs(l, r) 的作用是確定位置 l ~ r 的數字,而正在尋找的數字有兩種可能:
1. now 陣列中最大的數字減去 cnt 陣列中不為 0 的最大值。
2. cnt 陣列中不為 0 的最大值。
接著,使用 check 函式檢查這個數字是否符合條件。如果符合,就繼續對下一個數字進行 dfs 搜索。搜尋結束後,必須在回溯時將 check 中減去的 cnt 值補回去,以確保後續遞迴的正確性。
當 l > r 時,表示所有數字都已確定,因此將當前的數列分別與 ma 和 mi 取最大值與最小值,以確定最終的字典序排列。
```cpp=
#include <bits/stdc++.h>
using namespace std;
int cnt[101] = {};
int n;
vector<int> ma, mi, now;
int find() {
for (int i = 100;i >= 0;i--) {
if (cnt[i]) return i;
}
return -1;
}
bool check(int l, int r, int x) {
bool flag = 1;
for (int i = 0;i < l;i++) {
if (--cnt[abs(now[i] - x)] < 0) flag = 0;
}
for (int i = n - 1;i > r;i--) {
if (--cnt[abs(now[i] - x)] < 0) flag = 0;
}
return flag;
}
void back(int l, int r, int x) {
for (int i = 0;i < l;i++) {
cnt[abs(now[i] - x)]++;
}
for (int i = n - 1;i > r;i--) {
cnt[abs(now[i] - x)]++;
}
}
void dfs(int l, int r) {
if (l > r) {
mi = min(mi, now);
ma = max(ma, now);
return;
}
int big = find();
now[l] = now[n - 1] - big;
if (check(l, r, now[l])) dfs(l + 1, r);
back(l, r, now[l]);
now[r] = big;
if (check(l, r, now[r])) dfs(l, r - 1);
back(l, r, now[r]);
}
int main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
cin >> n;
if (n == 1) {
cout << 0 << '\n' << 0 << '\n';
return 0;
}
for (int i = 0;i < n * (n - 1) / 2;i++) {
int x;
cin >> x;
cnt[x]++;
}
ma.assign(n, 0);
mi.assign(n, 100);
now.assign(n, 0);
now[n - 1] = find();
cnt[now[n - 1]]--;
dfs(1, n - 2);
for (int i = 0;i < n;i++) cout << mi[i] << ' ';
cout << '\n';
for (int i = 0;i < n;i++) cout << ma[i] << ' ';
}
```
## [第四題 分組開會](https://zerojudge.tw/ShowProblem?problemid=q184)
由於這題的最大n為2×10^5^,所以無法使用O(n^2^)的解法。我們採用更高效的方法,一邊計算最小移動距離,一邊維護最佳答案。
計算最小移動距離的方法如下:
1. 找出這組的中位數(即排序後的第k/2個元素)。
2. 計算左右兩部分的總移動距離:
右半部分的總和 減去 左半部分的總和,即該組數列調整成中位數所需的總距離。
在每次檢查時,我們都額外考慮一種情況:
將最小移動距離加上 k,並與當前最佳答案 ans 取最小值,以確保找到全局最優解。
```cpp=
#include <bits/stdc++.h>
using namespace std;
#define int long long
vector<int> p;
int f(int a, int b) {
return p[b] - p[a - 1];
}
signed main() {
int n, k;
cin >> n >> k;
p.assign(n + 1, 0);
vector<int> v(n);
for (int i = 0;i < n;i++) cin >> v[i];
sort(v.begin(), v.end());
for (int i = 0;i < n;i++) p[i + 1] = p[i] + v[i];
int MIN = 1e18, ans = 1e18;
int h = k / 2;
for (int i = k;i + k <= n;i++) {
MIN = min(MIN, f(i - h + 1,i) - f(i - k + 1, i - k + h));
ans = min(ans, MIN + f(i - h + k + 1, i + k) - f(i + 1, i + h));
}
cout << ans;
}
```