传送门
题目描述
乔治有一些同样长的小木棍,他把这些木棍随意砍成几段,直到每段的长都不超过50。
现在,他想把小木棍拼接成原来的样子,但是却忘记了自己开始时有多少根木棍和它们的长度。
给出每段小木棍的长度,编程帮他找出原始木棍的最小可能长度。
输入输出格式
输入格式:输入文件共有二行。
第一行为一个单独的整数N表示砍过以后的小木棍的总数,其中N≤65
第二行为N个用空个隔开的正整数,表示N根小木棍的长度。
输出文件仅一行,表示要求的原始木棍的最小可能长度
输入输出样例
输入样例:
9
5 2 1 5 2 1 5 2 1
输出样例:
6
题解
深搜加剪枝不解释,具体做法看代码注释。
Code:
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
int n,tot=0,sum=0;
int a[70];
bool v[70],bj=false;
bool cmp(int x,int y)
{
return x>y;
}
bool dfs(int m,int l,int len,int last) //m表示当前已经搜到了多少根木棍,l表示当
{ //前拼接的木棍离目标长度的距离,len表示目
if(l==0 && m==tot) return true; //标长度,last表示上一根搜到的木棍的编号
if(l==0) l=len; //如果上一根木棍已拼接完成,则重新开始
int t=1;
if(l!=len) t=last; //若l!=len,则说明这不是当前木棍的第一根小木棍
for(int i=t;i<=tot;i++) //而排在上一根小木棍前的小木棍就不需要考虑
{
if(!v[i] && a[i]<=l)
{
if(i>1 && !v[i-1] && a[i]==a[i-1]) continue;
//如果上一根小木棍的长度与这一根一样,且上一根没有用过,则这一根也不考虑
v[i]=true; //标记为没有用过
if(dfs(m+1,l-a[i],len,i)) return true;
else
{
v[i]=false;
if(l==len || l==a[i]) return false;
//l==len是指如果搜索了一大圈都不成功,则这不能作为答案
//l==a[i]是指如果着一根长度正适合的都不能用到,则其他的不考虑
}
}
}
return false;
}
int main()
{
memset(v,false,sizeof(v));
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
int x;
scanf("%d",&x);
if(x>50) continue; //滤掉长度大于50的木棍
a[++tot]=x;
sum+=x;
}
sort(a+1,a+1+tot,cmp); //按照长度从大到小排序,由于每一根木棍都要用到,
//所以我们从最不好解决的,也就是最长的开始搜
for(int i=a[1];i<=sum/2;i++) //枚举原始木棍的长度,如果枚举到sum/2都没有
{ //答案,则只能拼成一根木棍
if(sum%i!=0) continue;
if(sum%2==1 && i%2==0) continue;
if(dfs(0,i,i,0))
{
printf("%d",i);
bj=true;
return 0;
}
}
if(!bj) printf("%d",sum);
}