TIOJ-1537

$n$筆測資

給一個數字$L$,輸出所有集合,滿足集合內數字總和為$L$,集合內的數字皆為正整數,且集合大小大於一。

輸出由數字小到數字大,且每個集合按照輸出的字典序排。

$1\le L\le 25, 1\le n\le 100$


這似乎是某種經典問題,好像是整數拆分ㄅ。
只要遞迴爆搜就好。
但根據題目要求得把答案全部存起來,排序,再把重複的拿掉。
那如何實作才能避免這樣呢?
其實觀察到如果要根據答案輸出的話,所有數字都不可比前一個數字大(否則違反由小到大),且越前面的數字會越多越小的數字(字典序)。
所以只要在遞迴時紀錄最後一項的答案,從那一個數字開始枚舉到當前所剩數字,終止條件為當前所剩數字等於0。

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
48
49
50
51
52
53
54
55
56
/*
-------------- | /
| | /
| | /
| * |/ | | ------ *
| | | | / \
| | |\ | | | |\ |
\ | | | \ | | | | \ |
\ | | | \ | | \ / \ |
V | | \ \__/| ----- \ |
*/
#include <bits/stdc++.h>
using namespace std;

#define EmiliaMyWife ios::sync_with_stdio(0); cin.tie(NULL);
using ll = int64_t;
using ull = uint64_t;
using ld = long double;
using uint = uint32_t;
const double EPS = 1e-8;
const int INF = 0x3F3F3F3F;
const ll LINF = 4611686018427387903;
const int MOD = 1e9+7;
/*--------------------------------------------------------------------------------------*/

vector<int> ans;
void solve(int n, int bg) {
if(!n) {
if(ans.size() <= 1)
return;
for(int i = 0; i < ans.size(); i++) {
cout << ans[i];
if(i + 1 < ans.size())
cout << ", ";
else
cout << '\n';
}
return;
}
for(int i = bg; i <= n; i++) {
ans.push_back(i);
solve(n - i, i);
ans.pop_back();
}
}

signed main() { EmiliaMyWife
int t;
cin >> t;
for(int i = 1; i <= t; i++) {
int n;
cin >> n;
cout << "Plank " << i << ":\n";
solve(n, 1);
}
}