# APCS實作題2023年1月第3題:先加後乘與函數
> 日期:2024年8月7日
> 作者:王一哲
> [ZeroJudge 題目連結](https://zerojudge.tw/ShowProblem?problemid=j607)
## 題目
### 問題描述
給一個運算式,運算式的內容由數字、+、\*和某個函式 $f()$ 所組成,除了函式 $f()$ 以外不會有額外的括號。請將此運算式依照**先加後乘**的方式運算。
函式 $f(x_1, x_2, x_3, x_4, \dots)$ 定義為從這個不定長度的參數 $x_1, x_2, x_3, x_4, \dots$ 中的最大值扣掉最小值。例如 $f(3, 2, 6) = 6-2=4$、$f(3)=0$。
<br />
### 輸入說明
輸入一個運算式,保證長度不超過 $500$,出現在運算式內的數字介於 0 到 200 之間,除了函式 $f()$ 之外不會出現多餘的括號,並且運算式一定合法。
- 30分:運算式只包含數字、+和\*
- 70分:無其他限制
<br />
### 輸出格式
輸出運算式的計算結果,此題運算過程和答案可能超過 $2^{31}$ 但不超過 $10^{17}$。
<br />
### 範例輸入1
```
2+3*1+2+1
```
### 範例輸出1
```
20
```
### 範例輸入2
```
12+f(13,2+f(8,1+2*3),1+1*f(20,4)*f(2))*2
```
### 範例輸出2
```
50
```
### 範例輸入3
```
f(0)
```
### 範例輸出3
```
0
```
<br /><br />
## Python 程式碼
費時最久約為 44 ms,使用記憶體最多約為 3.6 MB,通過測試。
```python=
import sys
""" 自訂函式,主要的解題過程 """
def solve(s):
cur = 0 # 暫存目前相加的結果
mul = [] # 最後要相乘的數值
num = "" # 目前正在讀取的數字
func = "" # f() 的內容,包含開頭的 f(
cnt = 0 # 待配對的 ( 數量
if s[0] == "f": # 如果是 f,開始處理 f(n)
imin = sys.maxsize # f(n) 中的最小值,預設為系統最大值
imax = -sys.maxsize # f(n) 中的最大值,預設為系統最大值加負號
cnt = 1 # 目前有一個 ( 待配對
for i in range(2, len(s)): # s 為 f(...),從 i=2 開始向右掃過
if s[i] == "(": cnt += 1 # 遇到 ( 加 1
elif s[i] == ")": cnt -= 1 # 遇到 ) 減 1
if i == len(s)-1 or (cnt == 1 and s[i] == ","): # 如果是最後一位或是只剩一組 () 且 s[i] 是逗號
tmp = solve(num) # 遞迴,代入 num
imin = min(imin, tmp) # 計算 f() 中的最小值
imax = max(imax, tmp) # 計算 f() 中的最大值
num = "" # 清空 num
else: num += s[i] # 不是以上的狀況,更新 num
return imax - imin # 回傳 f() 計算結果
else: # 不是 f
flag = False # 是否遇到 f,預設為 False
for i in range(len(s)+1): # 依序産生 0 ~ len(s),避免少讀最後一位
if i < len(s) and (s[i] == "f" or flag): # 如果遇到 f
func += s[i] # 更新 func
flag = True # 更新 flag
if s[i] == "(": cnt += 1 # 遇到 ( 加 1
elif s[i] == ")":
cnt -= 1 # 遇到 ) 減 1
if cnt == 0: # 遞迴出口,已經沒有待配對的 (
cur += solve(func) # 遞迴,代入 func 計算數值,加到 cur
func = "" # 清空 func
flag = False # 重設 flag
else: # 如果不是 f
if i == len(s) or s[i] == "*": # 如果已讀完 s 的最後一位或是遇到 *
if num: cur += int(num) # 如果 num 有內容,轉成整數加到 cur
mul.append(cur) # cur 加到 mul
num = "" # 清空 num
cur = 0 # 重設 cur
elif s[i] == "+": # 如果遇到 +
if num: cur += int(num) # 如果 num 有內容,轉成整數加到 cur
num = "" # 清空 num
elif s[i] in "0123456789": # 如果 s[i] 是數字
num += s[i] # 更新 num
# 處理完 s 的內容,最後將 mul 相乘求答案
if not mul: return 0 # 如果 mul 是空的,回傳 0
else:
ans = 1 # 答案,預設值為 1
for m in mul: ans *= m # 依序取出 mul 的資料與 ans 相乘
return ans # 回傳答案
""" 結束自訂函式 """
line = input() # 讀取運算式
print(solve(line)) # 呼叫 solve,代入 line,印出答案
```
<br /><br />
## C++ 程式碼
費時最久約為 9 ms,使用記憶體最多約為 448 kB,通過測試。
```cpp=
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
#include <climits> // for LLONG_MAX and LLONG_MIN
typedef long long LL; // 簡化為 LL
using namespace std;
/* 自訂函式,主要的解題過程 */
LL solve(string s) {
LL cur = 0; // 暫存目前相加的結果
int cnt = 0; // 待配對的 ( 數量
string num = "", func = ""; // 目前正在讀取的數字 num;f() 的內容,包含開頭的 f(
vector<LL> mul; // 最後要相乘的數值
if (s[0] == 'f') { // 如果是 f,開始處理 f(n)
LL imin = LLONG_MAX, imax = LLONG_MIN; // f(n) 中的最小值及最大值
cnt = 1; // 目前有一個 (
for(size_t i=2; i<s.size(); i++) { // s 為 f(...),從 i=2 開始向右掃過
if (s[i] == '(') cnt++; // 遇到 ( 加 1
else if (s[i] == ')') cnt--; // 遇到 ) 減 1
if (i == s.size()-1 || (cnt == 1 && s[i] == ',')) { // 如果是最後一位或是只剩一組 () 且 s[i] 是逗號
LL tmp = solve(num); // 遞迴,代入 num
imin = min(imin, tmp); // 計算 f() 中的最小值
imax = max(imax, tmp); // 計算 f() 中的最大值
num = ""; // 清空 num
} else { // 不是以上的狀況,更新 num
num += s[i];
}
}
return imax - imin; // 回傳 f() 計算結果
} else { // 不是 f
bool flag = false; // 是否遇到 f,預設為 false
for(size_t i=0; i<=s.size(); i++) { // 依序産生 0 ~ len(s),避免少讀最後一位
if (i < s.size() && (s[i] == 'f' || flag)) { // 如果遇到 f
func += s[i]; // 更新 func
flag = true; // 更新 flag
if (s[i] == '(') cnt++; // 遇到 ( 加 1
else if (s[i] == ')') {
cnt--; // 遇到 ) 減 1
if (cnt == 0) { // 遞迴出口,已經沒有待配對 (
cur += solve(func); // 遞迴,代入 func 計算數值,加到 cur
func = ""; // 清空 func
flag = false; // 重設 flag
}
}
} else { // 如果不是 f
if (i == s.size() || s[i] == '*') { // 如果已讀完 s 的最後一位或是遇到 *
if (!num.empty()) cur += stoi(num); // 如果 num 有內容,轉成整數加到 cur
mul.push_back(cur); // cur 加到 mul
num = ""; // 清空 num
cur = 0; // 重設 cur
} else if (s[i] == '+') { // 如果遇到 +
if (!num.empty()) cur += stoi(num); // 如果 num 有內容,轉成整數加到 cur
num = ""; // 清空 num
} else if (s[i] >= '0' && s[i] <= '9') { // 如果 s[i] 是數字
num += s[i]; // 更新 num
}
}
}
// 處理完 s 的內容,最後將 mul 相乘求答案
if (mul.empty()) return 0; // 如果 mul 是空的,回傳 0
else {
LL ans = 1; // 答案,預設值為 1
for(auto m : mul) ans *= m; // 依序取出 mul 的資料與 ans 相乘
return ans; // 回傳答案
}
}
}
int main() {
string line; cin >> line; // 讀取運算式
cout << solve(line) << "\n"; // 呼叫 solve,代入 line,印出答案
return 0;
}
```
<br /><br />
---
###### tags:`APCS`、`Python`、`C++`