167 木棒

乔治拿来一组等长的木棒,将它们随机地砍断,使得每一节木棍的长度都不超过50个长度单位。

然后他又想把这些木棍恢复到为裁截前的状态,但忘记了初始时有多少木棒以及木棒的初始长度。

请你设计一个程序,帮助乔治计算木棒的可能最小长度。

每一节木棍的长度都用大于零的整数表示。

注意: 数据中可能包含长度大于50的木棒,请在处理时忽略这些木棒。

输入格式

输入包含多组数据,每组数据包括两行。

第一行是一个不超过64的整数,表示砍断之后共有多少节木棍。

第二行是截断以后,所得到的各节木棍的长度。

在最后一组数据之后,是一个零。

输出格式

为每组数据,分别输出原始木棒的可能最小长度,每组数据占一行。

输入样例:

9
5 2 1 5 2 1 5 2 1
4
1 2 3 4
0

输出样例:

6
5

 

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std ;
const int MAX= 10005 ;
int a[MAX] ;
int n ;
int vis[MAX] ;
int cnt ;
int len ;
bool cmp(const int &x , const int &y) {
return x>y ;
}
bool dfs(int stick , int cab , int last){
// 当前正在拼第stick 根, 当前凑到cab长度了,
if(stick > cnt) return true ; // 如果当前已经拼完cnt根,说明可以
if(cab == len ) return dfs(stick+1,0,1) ; // 如果当前已经拼到len 了, 再凑下一根木棍
int fail = 0 ;// 记录上一次失败的木根
for(int i = last ; i<=n ; i++ ) {
if(!vis[i] && cab +a[i] <= len && fail !=a[i]){
// 如果还没有用过这个木根,
// 且 当前长度 + 这个木根的长度小于等于答案
vis[i] = 1 ;
if(dfs(stick,cab+a[i],i+1))return true ;
// 回溯,
fail = a[i] ; //剪枝,
vis[i] = 0 ;
if(cab == 0 ) return false; // 如果在一根原始木棒中尝试拼接的第一根木棍的递归分支就以失败返回
}
}
return false ; // 所有分支都尝试过,没找到,

}
int main(){
while(cin >> n && n) {
int sum = 0 ,val = 0 ;
for(int i = 1 ; i<=n ;i++ ) {
scanf("%d",&a[i]);
sum+=a[i] ;
val = max(val,a[i]) ;
}
sort(a+1 ,a+1+n,cmp) ;
// 从最小的可能开始枚举答案
for( len = val ; len <=sum ; len++) {
if(sum%len == 0 ){
cnt = sum/len ; // 原始有几根木棍
memset(vis,0,sizeof(vis)) ;
if(dfs(1,0,1)) break ;
}
}
cout<<len <<endl;
}


return 0 ;
}