题意
乔治拿了相同长度的木棍,随机切开,直到所有零件的长度最大为50个单位。 现在,他想将木棍恢复到原始状态,但是他忘记了原来拥有多少木棍以及它们原本有多长。 请帮助他,设计一个程序,计算出那些棍子的可能的原始最小长度。 所有以单位表示的长度都是大于零的整数。
输入
输入包含2行。 第一行包含切割后的木棍零件数量,最多为64个木棍。 第二行包含被空格隔开的那些部分的长度。 文件的最后一行包含零。
输出
输出应包含原始木棍的最小长度,每行一根。
Sample Input
9
5 2 1 5 2 1 5 2 1
4
1 2 3 4
0
Sample Output
6
5
题意: ·····恢复到原来的多根相同的木棍(或只有一根),求原来的木棍长度的最小值。
分析:
设: 切开后木棍所有长度 为一个数组 a ,所有的长度和为sum 。
原状态的木棍长度只可能是 [ 数组中的最大值,sum ] 之间能整除sum的数,求最小值就从 这些能整除sum的 的第一个数开始 依次判断该长度x是否满足条件 如满足则原始长度的最小值就是x
(此处 “满足条件” 就是:恰好构成了 num(num=sum/x) 根长度为x的木棍)
过题的关键在 dfs+剪枝 :
- 在尽可能靠近跟的地方剪枝:降序排序
- 当前一根原始木棍拼凑成功,后面一根拼凑失败就不用继续下去了
- 若一根原始木棍中的第一根小木棍不能成功拼凑这一跟原始木棍,就不用继续了
AC代码
#include <bits/stdc++.h>
using namespace std;
int n,sum,x,num;
int a[70],v[70];
bool cmp(int &a,int &b){return a>b;}
bool dfs(int cnt,int len,int cntnum){//cnt-当前组的第几根,cntnum-到了第几组,len-当前组的长度
if(cntnum==num)return 1;//枚举所有木棍有解( 构成了num根均为长度x的木棍)
for(int i=cnt;i<n;i++){
if(v[i] ||( i && !v[i-1] && a[i]==a[i-1]) )continue;//若该木棍被用过 或 该木棍跟前一根木棍一样且前一根没用过 就跳过
if(len+a[i]==x){
v[i]=1;
if(dfs(0,0,cntnum+1))return 1;
v[i]=0;
return 0;//剪枝2
}
else if(len+a[i]<x){
v[i]=1;
if(dfs(i+1,len+a[i],cntnum))return 1;
v[i]=0;
if(len==0)return 0;//剪枝3
}
}
return 0;//枚举所有木棍都没得到解,返回0
}
int main(){
while(scanf("%d",&n)){
if(n==0)break;
sum=0;
memset(a,0,n*sizeof(int));
for(int i=0;i<n;i++){scanf("%d",&a[i]);sum+=a[i];}
sort(a,a+n,cmp);
for(x=a[0];x<=sum;x++){
if(sum%x==0){
num=sum/x;//num为原来木棍根数
memset(v,0,sizeof(v));
if(dfs(0,0,0)){break;}
}
}
printf("%d\n",x);
}
return 0;
}
如果哪里写错了 或者 有更好的方法 可以私我让我也学习学习