#include <iostream>
#include <algorithm>
using namespace std;
const int MAXN = 70;
int n, m; // n 为输入总数,m 为过滤后的有效木棍数
int a[MAXN]; // 存储各小木棍长度
bool vis[MAXN]; // 标记某根小木棍是否已经被使用
int sum = 0; // 所有有效小木棍的总长度
int max_len = 0; // 最长的一根小木棍的长度
// 降序排序比较函数
bool cmp(int x, int y) {
return x > y;
}
/**
* 核心 DFS 函数
* @param cnt: 当前已经成功拼好的原木棍数量
* @param cur: 当前正在拼的这根原木棍已经拼凑出的长度
* @param last: 上一根被选中的小木棍的下标(用于规定顺序,避免重复组合)
* @param target: 我们当前正在尝试的原木棍目标长度
*/
bool dfs(int cnt, int cur, int last, int target) {
// 边界条件:如果拼出的总长度等于总长度,说明成功了
if (cnt * target == sum) return true;
// 如果当前正在拼的木棍达到了目标长度,开启下一根的拼凑
// 下一根的起点长度为0,且由于是新的一根,从头(下标1)开始找小木棍
if (cur == target) return dfs(cnt + 1, 0, 1, target);
for (int i = last; i <= m; ++i) {
// 如果这根木棍用过了,或者加上它会超出目标长度,直接跳过
if (vis[i] || cur + a[i] > target) continue;
vis[i] = true;
if (dfs(cnt, cur + a[i], i + 1, target)) return true;
vis[i] = false; // 回溯:如果往下走失败了,把这根木棍退回来
/* ================= 核心剪枝区域 ================= */
// 剪枝 1:失败位置是当前木棍的“第一根”
// 如果当前正在尝试拼一根新木棍(cur == 0),并且第一根就失败了,
// 那么这根小木棍迟早要用到某个地方,既然放第一根都不行,放后面更不行,直接放弃整个分支。
if (cur == 0) return false;
// 剪枝 2:失败位置是当前木棍的“最后一根”
// 如果放上这根小木棍刚好达到了 target(cur + a[i] == target),但后续搜索失败了,
// 说明用这根完美的整木棍都无解,如果把它拆碎用更多更小的木棍来填满这个空隙,只会更没有灵活性,必定也无解。
if (cur + a[i] == target) return false;
// 剪枝 3:跳过等效长度的木棍
// 如果当前长度为 a[i] 的木棍没有成功,那么下一根和它一样长的木棍也肯定不会成功。
while (i < m && a[i] == a[i + 1]) ++i;
}
return false;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n;
for (int i = 1; i <= n; ++i) {
int val;
cin >> val;
a[++m] = val;
sum += val;
max_len = max(max_len, val);
}
// 剪枝 4:优化搜索顺序(从大到小排序)
// 优先尝试长木棍,因为长木棍的灵活性差(能填进去的缝隙少),先安排它们能大幅减少搜索分支。
sort(a + 1, a + m + 1, cmp);
// 原始木棍的长度 len 最小是切出木棍中的最大值 max_len,最大是所有木棍总和 sum
for (int len = max_len; len <= sum; ++len) {
// 剪枝 5:可行性剪枝
// 如果总长度不能被当前目标长度整除,说明无法拼出整数根,直接跳过
if (sum % len != 0) continue;
// 开始尝试目标长度 len。每次 dfs 成功会直接退出,
// 失败的话 vis 数组会在回溯时清空,所以不需要 memset。
if (dfs(0, 0, 1, len)) {
cout << len << "\n";
break; // 从小到大枚举,遇到的第一个成功的即为最小值
}
}
return 0;
}