|
|
// #include <bits/stdc++.h>
|
|
|
#include <cstdio>
|
|
|
#include <iostream>
|
|
|
#include <cstring>
|
|
|
#include <algorithm>
|
|
|
using namespace std;
|
|
|
const int N = 70;
|
|
|
int a[N]; // 用来装每个木棍的长度
|
|
|
int n; // 木棍个数
|
|
|
int len; // 组长度
|
|
|
int sum; // 总长度
|
|
|
bool st[N]; // 标识某个木棍是不是使用过了
|
|
|
|
|
|
/*
|
|
|
u :木棒号,下标从0开始
|
|
|
last:最后一个木棒目前的长度
|
|
|
start:从哪个索引号开始继续查找
|
|
|
|
|
|
只要搜索到解就停止的dfs问题,一般用bool类型作为返回值,因为这样,搜到最优解就返回true,用dfs的返回值作为条件,
|
|
|
就可以瞬间退出递归了。比上一题中专门设置标志变量的方法要更好。
|
|
|
*/
|
|
|
bool dfs(int u, int last, int start) {
|
|
|
// 因为填充的逻辑是填充满了一个,才能走到下一个面前,所以如果成功到达了第u个前面的话,说明前u-1个都是填充满的
|
|
|
// 如果在第u个面前,检查到木棒长度 乘以 木棒数量 等于总长度,说明完成了所有填充工作,递归终止
|
|
|
if (len * u == sum) return true;
|
|
|
|
|
|
// 因为每一个木棒原来都是客观存在的,所以,每组木棍必须可以填充满一个木棒
|
|
|
// 不能填充满,就不能继续填充下一个木棒
|
|
|
if (last == len) return dfs(u + 1, 0, 0); // 注意当一组完成时,下一组从0开始搜
|
|
|
|
|
|
// 在当前木棒没有填充满的情况下,需要继续找某个木棍进行填充
|
|
|
// start表示搜索开始的位置
|
|
|
// 防止出现 abc ,cba这样的情况,我们要的是组合,不要排列,每次查找选择元素后面的就可以了
|
|
|
for (int i = start; i < n; i++) {
|
|
|
// 使用过 or 超过枚举的木棒长度
|
|
|
if (st[i] || last + a[i] > len) continue;
|
|
|
|
|
|
// 准备将i号木棍,放入u这个木棒中
|
|
|
st[i] = true; // 标识i号木棍已使用过
|
|
|
if (dfs(u, last + a[i], start + 1)) return true; // 将i号木棍放入u号木棒中
|
|
|
st[i] = false; // 恢复现场
|
|
|
|
|
|
// 可行性剪枝
|
|
|
// 优化4:如果在第u组放置失败,且此时第u组长度为0,这是最理想的状态,这种情况都放不下,那么占用了一些空间的情况下,就肯定更放不下!
|
|
|
if (last == 0) return false;
|
|
|
|
|
|
// 优化5:如果加入一个元素后某个分组和等于len了,但是后续搜索失败了(后续肯定是开新组并且last从0开始),则没有可行解,和4是等价的。
|
|
|
if (last + a[i] == len) return false;
|
|
|
|
|
|
// 优化6:冗余性剪枝
|
|
|
// 如果当前未放置成功,则后面和该木棒长度相等的也一样,直接略过即可
|
|
|
while (i < n - 1 && a[i] == a[i + 1]) i++;
|
|
|
}
|
|
|
return false;
|
|
|
}
|
|
|
int main() {
|
|
|
// 多组测试数据,以0结束输入
|
|
|
while (scanf("%d", &n) && n) {
|
|
|
// 多组数组初始化
|
|
|
sum = 0; // 木棒总长清零
|
|
|
memset(st, false, sizeof st); // 清空使用状态桶数组
|
|
|
|
|
|
// 录入n个木棍的长度
|
|
|
for (int i = 0; i < n; i++) scanf("%d", &a[i]), sum += a[i]; // 总长度
|
|
|
|
|
|
// 优化1:按小猫思路,由大到小排序,因为大卡车越往前越好找空放,越往后越不容易找空
|
|
|
// 根据搜索顺序优化,枚举长度小的分支比长度大的分支要多,所以先枚举长度大的
|
|
|
sort(a, a + n, greater<int>());
|
|
|
|
|
|
// 枚举每一个可能的木棒长度,注意这是一个全局量,因为需要控制递归的出口
|
|
|
for (len = a[0]; len <= sum; len++) { // 优化2:从最短的木棍长度开始,可以减枝
|
|
|
// 优化3:如果总长度不能整除木棒长度,那么不能按这个长度设置木棒长度
|
|
|
if (sum % len) continue;
|
|
|
|
|
|
// 在当前len长度的基础上,开始搜索
|
|
|
if (dfs(1, 0, 0)) {
|
|
|
printf("%d\n", len); // 找到最短的木棒长度
|
|
|
break; // 找到一个就退出
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
return 0;
|
|
|
} |