题目描述
乔治采取了相同长度的棍子并随机剪断,直到所有部分的长度最多为50个单位。现在他想把棍棒还原到原来的状态,但是他忘记了原来他有多少棍棒,以及他们原来的时间。请帮助他,并设计一个程序,计算这些棒的最小可能的原始长度。所有以单位表示的长度都是大于零的整数。
输入
输入包含2行的块。第一行包含切割后的零件数量,最多有64个零件。第二行包含由空格分隔的部分的长度。该文件的最后一行包含零。
输出
输出应包含原始棒的最小可能长度,每行一个。
样例输入
9
5 2 1 5 2 1 5 2 1
4
1 2 3 4
0
样例输出
6
5
题目大意:将n节木棒接成m个长度相等的木条,要求木条的长度尽可能的短。
解题思路:DFS没什么好说的了,说一下几个剪枝的地方。
<1>首先,拼成木条的长度必须是所有木棒长度总和的约数,并且大于等于木条中的最大值。
<2>将木棒从打到小排列,可以减少递归的层数。
<3>当第i个木棒未选取时,如果i+ 1木棒与i木棒相同长度。跳过, 没有必要重复考虑。
<4>当遍历到i个木棒时,找不到小木棒和它组成木条时, 可以终止当前判断的木条长度。
算法实现:
#include <iostream>
#include <algorithm>
#include <string.h>
#define N 105
using namespace std;
int n,ans,sum,stick[N],vis[N];
int cmp(const int&a,const int&b)
{
return a>b;
}
bool dfs(int cnt,int pos,int sum)//拼凑了几根木棒 当前进行拼凑木棒位置 木棒现在被拼凑后总长度
{
if(cnt==n) return true;//如果已经拼凑完了n根木棒 返回true
for(int i=pos;i<n;i++)//将当前木棒位置一直深搜到 最后一根木棒位置
{
if(vis[i]) continue;//如果当前木棒已经被选取过 跳过
if(sum+stick[i]<ans)//如果 拼凑以后的总长度小于 原木棒长度
{
vis[i]=1;//标记现在挑选的木棒
if(dfs(cnt+1,i+1,sum+stick[i]))//再进行深搜,如果成功 返回正确
return true;
vis[i]=0;
while(stick[i]==stick[i+1]&&i+1<n) i++;//如果下一个木棒和当前木棒长度一致 则跳过
}
else if(sum+stick[i]==ans)//如果拼凑以后的木棒等于 原木棒长度
{
vis[i]=1;//标记现在的木棒搜索过
if(dfs(cnt+1,0,0))//对剩下的木棒进行深搜 如果成功 返回成功
return true;
vis[i]=0;//不成功的话 将现在的木棒标记为未搜索过 返回失败
return false;
}
if(sum==0)//如果找不到可以和当前长度木棒进行拼凑短木棒 可以直接判断返回失败
return false;
}
return false;//如果搜索完了 仍然不行则返回失败
}
int main()
{
while(cin>>n,n!=0)
{
memset(stick,0,sizeof(stick));
sum=0;
for(int i=0;i<n;i++)//sum进行木棒总长求和
{
cin>>stick[i];
sum+=stick[i];
}
sort(stick,stick+n,cmp);//将短木棒从大到小排序
for(int i=n;i>0;--i)//原来总木棒数目最多为n最少为1
{
if(sum%i==0&&(sum/i)>=stick[0])//原来总木棒数一定会被总长整除 并且总长度一定会大于短木棒中最长的
{
ans=sum/i;//ans用来记录原木棒长度
memset(vis,0,sizeof(vis));//将vis全部置为0,未拼凑过
if(dfs(0,0,0))//搜索
{
cout<<ans<<endl;
break;
}
}
}
}
return 0;
}