题目
【 问题描述】
乔治有一些同样长的小木棍, 他把这些木棍随意砍成几段, 直到每段的长都
不超过 50。
现在, 他想把小木棍拼接成原来的样子, 但是却忘记了自己开始时有多少根
木棍和它们的长度。
给出每段小木棍的长度, 编程帮他找出原始木棍的最小可能长度。
【 输入】
输入文件共有二行。
第一行为一个单独的整数 N 表示砍过以后的小木棍的总数, 其中 N≤60, 第
二行为 N 个用空格隔开的正整数, 表示 N 根小木棍的长度。
【 输出】
输出文件仅一行, 表示要求的原始木棍的最小可能长度。
【 样例】
stick.in
9
5 2 1 5 2 1 5 2 1
stick.out
6
题解
#include<stdio.h>
#include<stdlib.h>
#include<algorithm>
int cnt,n,a[61],sum,maxx;
int max(int x,int y){
return x>y?x:y;
}// 判断大小
void init(){
int x;
for(int i=1;i<=n;i++){
scanf("%d",&x);
if(x<=50){//当木棍长度大于50时,不能使用
a[++cnt]=x;
sum+=x;//求和
maxx=max(x,maxx);//求最大的木棍长度
}
}
}
int flag;
int b[61];
int t,m;
void search(int l,int s){//l是我枚举到了原始木棍根数的第l根
//s是我现在这一根枚举到的长度
if(flag)return;//不断弹出
if(l==t){//当完成时
flag=1;//标记
return;//弹出
}
if(s==m){//当到达目标时
l++;//l累加
s=0;//重新计算s
}
for(int i=1;i<=cnt;i++){//枚举每一个可能的木棍
if(a[i]+s>m || b[i])continue;//可行性
if(a[i-1]==a[i] && !b[i])continue;//可行性剪枝(参考解题报告第2条)
b[i]=1;//标记
search(l,s+a[i]);//接着搜
b[i]=0;//回溯
if(flag)return;//如果完成了,弹出
if(s==0)return;//当我有完成组,但是却没有全部完成时,也弹出
//两者可以交换位置
}
}
int comp(const int &a,const int &b){
return a>b;
}//快排从大到小
int main(){
freopen("stick.in","r",stdin);
freopen("stick.out","w",stdout);//文件
int i,j,k;
scanf("%d",&n);
init();
std::sort(a+1,a+n+1,comp);//从大到小排序,方便匹配
for(i=maxx;i<=sum;i++){//从木棍最长的那一根到总和(剪枝)
if(sum%i==0){//如果可行
t=sum/i;//t是原始木棍有t根
m=i;//m是原始木棍每一根的长度
flag=0;//标记清零(可以不清)
search(0,0);//搜索
if(flag){//如果这一种原始木棍的解可行
printf("%d\n",i);//输出
return 0;//结束程序
}
}
}
return 0;//可有可无
}