# C++ 基礎語法練習題:Unit-10 遞迴 (Recursion)
講義不是我寫的,原文連結為 [Yui Huang 演算法學習筆記:C++ 基礎語法](https://yuihuang.com/syntax/)
我只是將自己寫的練習題程式碼記錄下來。
最後更新日期:2024年11月15日
## [ZeroJudge: c002. 10696 - f91](https://zerojudge.tw/ShowProblem?problemid=c002)
### Python 程式碼
執行時間最久約為 25 ms,使用記憶體最多約為 3.3 MB,通過測試。
```python=
def f91(x):
if x >= 101: return x-10
return f91(f91(x+11))
while True:
n = int(input())
if n == 0: break
print(f"f91({n:d}) = {f91(n):d}")
```
### C++ 程式碼
執行時間最久約為 2 ms,使用記憶體最多約為 348 kB,通過測試。
```cpp=
#include <iostream>
using namespace std;
int f91(int x) {
if (x >= 101) return x-10;
return f91(f91(x+11));
}
int main() {
ios::sync_with_stdio(0); cin.tie(0);
while(true) {
int n; cin >> n;
if (n == 0) break;
cout << "f91(" << n << ") = " << f91(n) << "\n";
}
return 0;
}
```
## [ZeroJudge: c813. 11332 - Summing Digits](https://zerojudge.tw/ShowProblem?problemid=c813)
### Python 程式碼
執行時間最久約為 0.1 ms,使用記憶體最多約為 3.3 MB,通過測試。
```python=
def f(x):
tot = 0
while x > 0:
tot += x%10
x //= 10
return tot
def g(x):
tot = f(x)
if 1 <= tot <= 9: return tot
return g(tot)
while True:
n = int(input())
if n == 0: break
print(g(n))
```
### C++ 程式碼
執行時間最久約為 7 ms,使用記憶體最多約為 340 kB,通過測試。
```cpp=
#include <iostream>
using namespace std;
int f(int x) {
int tot = 0;
while(x > 0) {
tot += x%10;
x /= 10;
}
return tot;
}
int g(int x) {
int tot = f(x);
if (tot >= 1 && tot <= 9) return tot;
return g(tot);
}
int main() {
ios::sync_with_stdio(0); cin.tie(0);
while(true) {
int n; cin >> n;
if (n == 0) break;
cout << g(n) << "\n";
}
return 0;
}
```
## [ZeroJudge: f640. 函數運算式求值](https://zerojudge.tw/ShowProblem?problemid=f640)
### Python 程式碼
遞迴版,執行時間最久約為 19 ms,使用記憶體最多約為 3.3 MB,通過測試。
```python=
data = list(input().split())
idx = 0
def solve():
global idx
s = data[idx]
if s == "f":
idx += 1; x = solve()
return 2*x - 3
elif s == "g":
idx += 1; x = solve()
idx += 1; y = solve()
return 2*x + y - 7
elif s == "h":
idx += 1; x = solve()
idx += 1; y = solve()
idx += 1; z = solve()
return 3*x - 2*y + z
else:
return int(s)
print(solve())
```
### C++ 程式碼
遞迴版,執行時間最久約為 3 ms,使用記憶體最多約為 376 kB,通過測試。
```cpp=
#include <iostream>
#include <vector>
#include <string>
using namespace std;
vector<string> data;
int idx = 0;
int solve() {
string s = data[idx];
if (s == "f") {
idx++; int x = solve();
return 2*x - 3;
} else if (s == "g") {
idx++; int x = solve();
idx++; int y = solve();
return 2*x + y - 7;
} else if (s == "h") {
idx++; int x = solve();
idx++; int y = solve();
idx++; int z = solve();
return 3*x - 2*y + z;
} else {
return stoi(s);
}
}
int main() {
ios::sync_with_stdio(0); cin.tie(0);
string s;
while(cin >> s) data.push_back(s);
cout << solve() << "\n";
return 0;
}
```
## [ZeroJudge: e446. 排列生成](https://zerojudge.tw/ShowProblem?problemid=e446)
### Python 程式碼
遞迴解,第8筆測資開始超時。
```python=
def perm(depth, maxDepth, nums, tested):
if depth == maxDepth:
print(*nums)
return
for i in range(1, N+1):
if i in tested: continue
tested.add(i)
nums.append(i)
perm(depth+1, maxDepth, nums, tested)
tested.remove(i)
nums.pop()
N = int(input())
perm(0, N, [], set())
```
### C++ 程式碼
遞迴解,執行時間最久約為 9.3 s,使用記憶體最多約為 344 kB,通過測試。
```cpp=
#include <iostream>
#include <vector>
#include <set>
using namespace std;
int N;
void perm(int depth, int maxDepth, vector<int>& nums, set<int>& tested) {
if (depth == maxDepth) {
for(int i=0; i<(int)nums.size(); i++)
cout << nums[i] << " \n"[i == (int)nums.size()-1];
return;
}
for(int i=1; i<=N; i++) {
if (tested.count(i) == 1) continue;
tested.insert(i);
nums.push_back(i);
perm(depth+1, maxDepth, nums, tested);
tested.erase(i);
nums.pop_back();
}
}
int main() {
ios::sync_with_stdio(0); cin.tie(0);
cin >> N;
vector<int> nums;
set<int> tested;
perm(0, N, nums, tested);
return 0;
}
```
使用 algorithm next_permutation 作弊,執行時間最久約為 7.8 s,使用記憶體最多約為 352 kB,通過測試。
```cpp=
#include <iostream>
#include <algorithm>
using namespace std;
int main() {
ios::sync_with_stdio(0); cin.tie(0);
int N; cin >> N;
int nums[N];
for(int i=0; i<N; i++) {
nums[i] = i+1;
cout << i+1 << " \n"[i == N-1];
}
while(next_permutation(nums, nums+N)) {
for(int i=0; i<N; i++)
cout << nums[i] << " \n"[i == N-1];
}
return 0;
}
```
## [ZeroJudge: f168. m4a2-分配寶物(Treasure)](https://zerojudge.tw/ShowProblem?problemid=f168)
### Python 程式碼
寫法1,執行時間最久約為 18 ms,使用記憶體最多約為 3.3 MB,通過測試。
```python=
import sys
for line in sys.stdin:
n = int(line) # 寶物數量
vs = list(map(int, input().split())) # 寶物價值
vs.sort(reverse=True) # 由大到小排序
tot = sum(vs) # 寶物價值加總
if tot%3: print("NO") # 如果不能被3整除,印出 NO
else: # 如果可以被3整除
avg = tot//3 # 平均值
dis = [avg]*3 # 每個人要分到的寶物價值
i = 0 # 目前要拿寶物的人
for v in vs: # 依序由 vs 取出資料 v
cnt = 0 # 目前測試的人數
while dis[i] < v and cnt < 3: # 如果第 i 個人還需要分配的價值小於 v 而且還有人沒有測試過
i = (i+1)%3 # 更新 i,換下一個人,如果 i == 3 則輪回到 i == 0
cnt += 1 # 已測試人數加 1
if cnt == 3: break # 如果已測試 3 個人,中止迴圈
dis[i] -= v # 第 i 個人還需要分配的價值扣掉 v
i = (i+1)%3 # 更新 i,換下一個人,如果 i == 3 則輪回到 i == 0
# end of for loop
if dis[0] == dis[1] == dis[2] == 0: print("YES") # 如果 dis 內容都是 0,已平分,印出 YES
else: print("NO") # 否則印出 NO
```
寫法2,遞迴版,執行時間最久約為 27 ms,使用記憶體最多約為 3.4 MB,通過測試。
```python=
import sys
# 輸入寶物價值 vs,由 vs 讀資料的索引值 i,vs 長度 imax
# 每個人還要分配的寶物價值 dis,第 j 個人,jmax 為人數,cnt 為已測試人數
def solve(vs, i, imax, dis, j, jmax, cnt):
j %= jmax # 避免 j 出界
if i == imax or cnt == jmax: # 如果 i 已出界或每個人都已測試
if dis[0] == dis[1] == dis[2] == 0: return True # 如果 dis 內容都是 0,回傳 True
else: return False # 否則回傳 False
if dis[j] >= vs[i]: # 如果第 j 個人還要分配的寶物價值大於等於 vs[i]
dis[j] -= vs[i] # 扣掉 vs[i],再遞迴,測試下一個寶物及下一個人,cnt 歸零
return solve(vs, i+1, imax, dis, j+1, jmax, 0)
else: # 遞迴,測試這個寶物及下一個人,cnt 加 1
return solve(vs, i, imax, dis, j+1, jmax, cnt+1)
for line in sys.stdin:
n = int(line) # 寶物數量
vs = list(map(int, input().split())) # 寶物價值
vs.sort(reverse=True) # 由大到小排序
tot = sum(vs) # 寶物價值加總
if tot%3: print("NO") # 如果不能被3整除,印出 NO
else: # 如果可以被3整除
if solve(vs, 0, n, [tot//3]*3, 0, 3, 0): print("YES")
else: print("NO")
```
### C++ 程式碼
寫法1,執行時間最久約為 3 ms,使用記憶體最多約為 352 kB,通過測試。
```cpp=
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main() {
ios::sync_with_stdio(0); cin.tie(0);
int n; // 寶物數量
while(cin >> n) {
vector<int> vs; // vs 寶物價值
int tmp, tot = 0; // tmp 暫存用,tot 寶物價值加總
for(int i=0; i<n; i++) {
cin >> tmp; vs.push_back(tmp); tot += tmp;
}
sort(vs.begin(), vs.end(), greater<int>()); // 由大到小排序
if (tot%3) {
cout << "NO\n"; // 如果不能被3整除,印出 NO
} else { // 如果可以被3整除
int avg = tot/3; // 平均值
vector<int> dis (3, avg); // 每個人要分到的寶物價值
int i = 0; // 目前要拿寶物的人
for(int v : vs) { // 依序由 vs 取出資料 v
int cnt = 0; // 目前測試的人數
while(dis[i] < v && cnt < 3) { // 如果第 i 個人還需要分配的價值小於 v 而且還有人沒有測試過
i = (i+1)%3; // 更新 i,換下一個人,如果 i == 3 則輪回到 i == 0
cnt++; // 已測試人數加 1
}
if (cnt == 3) break; // 如果已測試 3 個人,中止迴圈
dis[i] -= v; // 第 i 個人還需要分配的價值扣掉 v
i = (i+1)%3; // 更新 i,換下一個人,如果 i == 3 則輪回到 i == 0
}
if (dis[0] == 0 && dis[1] == 0 && dis[2] == 0) {
cout << "YES\n"; // 如果 dis 內容都是 0,已平分,印出 YES
} else {
cout << "NO\n"; // 否則印出 NO
}
}
}
return 0;
}
```
寫法2,遞迴版,執行時間最久約為 2 ms,使用記憶體最多約為 336 kB,通過測試。
```cpp=
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
/* 輸入寶物價值 vs,由 vs 讀資料的索引值 i,vs 長度 imax
每個人還要分配的寶物價值 dis,第 j 個人,jmax 為人數,cnt 為已測試人數 */
bool solve(vector<int>& vs, int i, int imax, vector<int>& dis, int j, int jmax, int cnt) {
j %= jmax; // 避免 j 出界
if (i == imax || cnt == jmax) { // 如果 i 已出界或每個人都已測試
if (dis[0] == 0 && dis[1] == 0 && dis[2] == 0) return true; // 如果 dis 內容都是 0,回傳 true
else return false; // 否則回傳 false
}
if (dis[j] >= vs[i]) { // 如果第 j 個人還要分配的寶物價值大於等於 vs[i]
dis[j] -= vs[i]; // 扣掉 vs[i],再遞迴,測試下一個寶物及下一個人,cnt 歸零
return solve(vs, i+1, imax, dis, j+1, jmax, 0);
} else { // 遞迴,測試這個寶物及下一個人,cnt 加 1
return solve(vs, i, imax, dis, j+1, jmax, cnt+1);
}
}
int main() {
ios::sync_with_stdio(0); cin.tie(0);
int n; // 寶物數量
while(cin >> n) {
vector<int> vs; // 寶物價值
int v, tot = 0; // v 暫存寶物價值的變數,tot 寶物價值加總
for(int i=0; i<n; i++) {
cin >> v; vs.push_back(v); tot += v;
}
sort(vs.begin(), vs.end(), greater<int>()); // 由大到小排序
if (tot%3) { // 如果不能被3整除,印出 NO
cout << "NO\n";
} else { // 如果可以被3整除
vector<int> dis (3, tot/3);
if (solve(vs, 0, n, dis, 0, 3, 0)) cout << "YES\n";
else cout << "NO\n";
}
}
return 0;
}
```
## [ZeroJudge: d044. 00640 - Self Numbers](https://zerojudge.tw/ShowProblem?problemid=d044)
### Python 程式碼
使用 set,執行時間最久約為 2.7 s,使用記憶體最多約為 161.9 MB,通過測試。
```python=
def d(n):
tot = n
while n > 0:
tot += n%10
n //= 10
return tot
imax = 1000000
nums = set(range(1, imax+1))
tested = set()
for i in range(1, imax+1):
x = d(i)
if x in tested: continue
tested.add(x)
if x <= imax: nums.remove(x)
ans = sorted(list(nums))
for a in ans: print(a)
```
使用 list 記錄測試過的數字,執行時間最久約為 2.5 s,使用記憶體最多約為 13.7 MB,通過測試。
```python=
def d(n):
tot = n
while n > 0:
tot += n%10
n //= 10
return tot
imax = 1000000
tested = [False]*(imax+1)
for i in range(1, imax+1):
x = d(i)
if x > imax or tested[x]: continue
tested[x] = True
for x in range(1, imax+1):
if not tested[x]:
print(x)
```
### C++ 程式碼
使用 set,執行時間最久約為 0.5 s,使用記憶體最多約為 46.1 MB,通過測試。
```cpp=
#include <iostream>
#include <set>
using namespace std;
int d(int n) {
int tot = n;
while(n > 0) {
tot += n%10;
n /= 10;
}
return tot;
}
int main() {
ios::sync_with_stdio(0); cin.tie(0);
const int imax = 1000000;
set<int> nums, tested;
for(int i=1; i<=imax; i++) nums.insert(i);
for(int i=1; i<=imax; i++) {
int x = d(i);
if (tested.count(x) == 1) continue;
tested.insert(x);
nums.erase(x);
}
for(auto num : nums) cout << num << "\n";
return 0;
}
```
使用 vector 記錄測試過的數字,執行時間最久約為 32 ms,使用記憶體最多約為 432 kB,通過測試。
```cpp=
#include <iostream>
#include <vector>
using namespace std;
int d(int n) {
int tot = n;
while(n > 0) {
tot += n%10;
n /= 10;
}
return tot;
}
int main() {
ios::sync_with_stdio(0); cin.tie(0);
const int imax = 1000000;
vector<bool> tested(imax+1, false);
for(int i=1; i<=imax; i++) {
int x = d(i);
if (x > imax || tested[x]) continue;
tested[x] = true;
}
for(int i=1; i<=imax; i++) {
if (!tested[i]) {
cout << i << "\n";
}
}
return 0;
}
```
---
###### tags:`演算法`、`APCS`、`Python`、`C++`