#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
int len[75],used[75],ans,sum,n;
bool cmp(int a,int b)
{
return a>b;
}
bool check(int pos/*当前小木棍编号*/,int stk/*当前长木棍剩余长度*/,int alrest/*剩余小木棍长度总和*/)
{
if (stk==alrest) return true;
if (ans==alrest) return true;
int i;
for (i=1;i<=n;i++)
{
if (!used[i]&&len[i]<=stk)
{
used[i]=1;
if (len[i]==stk)
{if (check(1,ans,alrest-len[i])) return true;}
else if (check(pos+1,stk-len[i],alrest-len[i])) return true;
used[i]=0;
if (stk==len[i]) return false;//如果这根可以,但之后的不行,就不用继续搜了
if (alrest==sum) return false;//如果第一个不行就不用继续搜了
if (stk==ans) return false;//如果第一个不行就不用继续搜了
while (len[i]==len[i+1]) i++;//不重复搜
}
}
return false;
}
int main()
{
while (cin>>n)
{
int sum=0;
if (n==0) break;
for (int i=1;i<=n;i++)
{
cin>>len[i];
sum+=len[i];
used[i]=0;
}
sort(len+1,len+n+1,cmp);
ans=len[1];
int flag=0;
while (ans<=sum&&flag==0)
{
while (sum%ans!=0) ans++;
if (check(1,ans,sum))
{
cout<<ans<<endl;
flag=1;
}
ans++;
}
}
return 0;
}
/*题目大意:给出一些长度不大于 50 的木棍,
要求你把这些小木棍拼成长度相同木棍,当然长度越小越好。
解题思路:
思想很简单,一个接一个的把木棍拼起来,最后把木棍用光。
关键的地方是几个剪枝技巧:
设所有木棍的总长度为 Sum, 最终的答案是 L。
1. 首先要明白, Sum一定要能被 L 整除。
2. L 一定 大于等于 题目给出的最长的木棍的长度 Max。
由上述两点,我们想到,可以从 Max 开始递增地枚举 L,
直到成功地拼出 Sum/L 支长度为 L 的木棍。
搜索中的剪枝技巧:
3. 将输入的输入从大到小排序,这么做是因为一支长度为 K
的完整木棍,总比几支短的小木棍拼成的要好。形象一些:
如果我要拼 2 支长为8的木棍,第一支木棍我拼成 5 + 3
然后拼第二支木棍但是失败了,而我手中还有长为 2 和 1
的木棍,我可以用 5 + 2 + 1 拼好第一支,再尝试拼第二
支,仔细想一想,就会发现这样做没意义,注定要失败的。
我们应该留下 2+1 因为 2+1 比 3 更灵活。
我手中有一些木棍, 其中有 2 根长为 4 的木棍, 当前搜索
状态是 5+4+.... (即表示长度为 5,4,2 的三支拼在一起,
...表示深层的即将搜索的部分), 进行深搜后不成功,故我
没必要用另一个 4 在进行 5+4+...
5. 将开始搜索一支长为 L 的木棍时,我们总是以当前最长的未
被使用的 木棍开始,如果搜索不成功,那么以比它短的开始
那么也一定不能取得全局的成功。因为每一支题目给出的木棍
都要被用到。如果,有 4 5 4 4 3 2
想拼成长为 6 的木棍,那么从 5 开始, 但是显然没有能与 5
一起拼成 6 的,那么我就没必要去尝试从 4 开始的,因为
最终 5 一定会被遗弃。在拼第 2 3 ... 支木棍时,一样。
6. 最后的最简单的一个就是,
for(int i = 0; i < n; i++)
for(int j = 0; j < n; j++)
{}
与
for(int i = 0; i < n; i++)
for(int j = i+1; j < n; j++)
{}
的区别,这个不多说了。*/
#include <cstdio>
#include <algorithm>
using namespace std;
int len[75],used[75],ans,sum,n;
bool cmp(int a,int b)
{
return a>b;
}
bool check(int pos/*当前小木棍编号*/,int stk/*当前长木棍剩余长度*/,int alrest/*剩余小木棍长度总和*/)
{
if (stk==alrest) return true;
if (ans==alrest) return true;
int i;
for (i=1;i<=n;i++)
{
if (!used[i]&&len[i]<=stk)
{
used[i]=1;
if (len[i]==stk)
{if (check(1,ans,alrest-len[i])) return true;}
else if (check(pos+1,stk-len[i],alrest-len[i])) return true;
used[i]=0;
if (stk==len[i]) return false;//如果这根可以,但之后的不行,就不用继续搜了
if (alrest==sum) return false;//如果第一个不行就不用继续搜了
if (stk==ans) return false;//如果第一个不行就不用继续搜了
while (len[i]==len[i+1]) i++;//不重复搜
}
}
return false;
}
int main()
{
while (cin>>n)
{
int sum=0;
if (n==0) break;
for (int i=1;i<=n;i++)
{
cin>>len[i];
sum+=len[i];
used[i]=0;
}
sort(len+1,len+n+1,cmp);
ans=len[1];
int flag=0;
while (ans<=sum&&flag==0)
{
while (sum%ans!=0) ans++;
if (check(1,ans,sum))
{
cout<<ans<<endl;
flag=1;
}
ans++;
}
}
return 0;
}
/*题目大意:给出一些长度不大于 50 的木棍,
要求你把这些小木棍拼成长度相同木棍,当然长度越小越好。
解题思路:
思想很简单,一个接一个的把木棍拼起来,最后把木棍用光。
关键的地方是几个剪枝技巧:
设所有木棍的总长度为 Sum, 最终的答案是 L。
1. 首先要明白, Sum一定要能被 L 整除。
2. L 一定 大于等于 题目给出的最长的木棍的长度 Max。
由上述两点,我们想到,可以从 Max 开始递增地枚举 L,
直到成功地拼出 Sum/L 支长度为 L 的木棍。
搜索中的剪枝技巧:
3. 将输入的输入从大到小排序,这么做是因为一支长度为 K
的完整木棍,总比几支短的小木棍拼成的要好。形象一些:
如果我要拼 2 支长为8的木棍,第一支木棍我拼成 5 + 3
然后拼第二支木棍但是失败了,而我手中还有长为 2 和 1
的木棍,我可以用 5 + 2 + 1 拼好第一支,再尝试拼第二
支,仔细想一想,就会发现这样做没意义,注定要失败的。
我们应该留下 2+1 因为 2+1 比 3 更灵活。
我手中有一些木棍, 其中有 2 根长为 4 的木棍, 当前搜索
状态是 5+4+.... (即表示长度为 5,4,2 的三支拼在一起,
...表示深层的即将搜索的部分), 进行深搜后不成功,故我
没必要用另一个 4 在进行 5+4+...
5. 将开始搜索一支长为 L 的木棍时,我们总是以当前最长的未
被使用的 木棍开始,如果搜索不成功,那么以比它短的开始
那么也一定不能取得全局的成功。因为每一支题目给出的木棍
都要被用到。如果,有 4 5 4 4 3 2
想拼成长为 6 的木棍,那么从 5 开始, 但是显然没有能与 5
一起拼成 6 的,那么我就没必要去尝试从 4 开始的,因为
最终 5 一定会被遗弃。在拼第 2 3 ... 支木棍时,一样。
6. 最后的最简单的一个就是,
for(int i = 0; i < n; i++)
for(int j = 0; j < n; j++)
{}
与
for(int i = 0; i < n; i++)
for(int j = i+1; j < n; j++)
{}
的区别,这个不多说了。*/