题目描述
乔治拿来一组等长的木棒,将它们随机地砍断,使得每一节木棍的长度都不超过 50 个长度单位。
然后他又想把这些木棍恢复到为裁截前的状态,但忘记了初始时有多少木棒以及木棒的初始长度。
请你设计一个程序,帮助乔治计算木棒的可能最小长度。
每一节木棍的长度都用大于零的整数表示。
输入格式
输入包含多组数据,每组数据包括两行。
第一行是一个不超过 64 的整数,表示砍断之后共有多少节木棍。
第二行是截断以后,所得到的各节木棍的长度。
在最后一组数据之后,是一个零。
输出格式
为每组数据,分别输出原始木棒的可能最小长度,每组数据占一行。
数据范围
数据保证每一节木棍的长度均不大于 50。
样例
in:
9
5 2 1 5 2 1 5 2 1
4
1 2 3 4
0
out:
6
5
算法
dfs+剪枝
时间复杂度
O ( n k ) l o g m O(n^k)logm O(nk)logm
代码
#include<bits/stdc++.h>
using namespace std;
const int N = 70;
int sum,L;
int a[N],n;
bool v[N];
int len[N];
bool dfs(int u,int l,int cnt) {
if(u*L == sum) {
return true;
}
if(l == L) {
return dfs(u+1,0,0);
}
for(int i = cnt; i< n; ++i) {
if(v[i]) continue;
int t = a[i];
if(l+ t <= L) {
v[i] = 1;
if(dfs(u,l+t,i+1))return true;
v[i] = 0;
//剪枝不用选相同的不符合边
int j = i;
while(j<n&&t == a[j]) ++j;
i = j-1;
// 剪枝如果是第一个木棒失败,则一定失败
if(!cnt) return false;
//剪枝如果是最后个木棒失败,则一定失败
if(l+a[i] == L) {
return false;
}
}
}
return false;
}
int main( ) {
while(cin>>n,n) {
memset(v,0,sizeof v);
memset(len,0,sizeof len);
L = 0,sum = 0;
for(int i = 0 ; i < n ;++i) {
cin>>a[i];
L = max(L,a[i]);
sum += a[i];
}
sort(a,a+n);
reverse(a,a+n);
//从大的开始搜
while(1) {
if(sum%L==0&&dfs(0,0,0)) {
cout<<L<<endl;
break;
}
L++;
}
}
}