Description
George took sticks of the same length and cut them randomly until all parts became at most 50 units long. Now he wants to return sticks to the original state, but he forgot how many sticks he had originally and how long they were originally. Please help him and design a program which computes the smallest possible original length of those sticks. All lengths expressed in units are integers greater than zero.
Input
The input contains blocks of 2 lines. The first line contains the number of sticks parts after cutting, there are at most 64 sticks. The second line contains the lengths of those parts separated by the space. The last line of the file contains zero.
Output
The output should contains the smallest possible length of original sticks, one per line.
Sample Input
9 5 2 1 5 2 1 5 2 1 4 1 2 3 4 0
Sample Output
6 5
思路:
首先把小棍按长度,从大到小排序(贪心选择),并计算这些小棍的总长度,拼成的长棍的长度从最长的小棍开始搜索(最小的组合也会大于等于最长的那根),如果小棍的总长度能整除该长棍长度,则可能完成拼凑,问题简化成n根小棍,长度已知,拼成k根长度为Len的长棍,能否完成,若能,输出Len,结束;否则,增加长棍的长度,继续搜索,最坏的情况下,所有的小木棍一起,能拼成一根长木棍,所以,搜索终能成功。
现在在问题是,如何判断n根小棍,能够拼成长度为Len的k根长棍。dfs函数解决这个问题。每次凑长棍的过程,可以看成是从未使用的小棍 (长度大到小排序)中进行尝试,贪心原则是,如果有比较长的小棍能满足条件,一定先选上。一次凑长棍的过程:第1根小棍,从0..n-1(未用部分)中尝试,第2根小棍则只需要从1..n-1中尝试,因为第0根小棍要么以前凑长棍的时候就被用了,否则在选第一根小棍的时候一定选上了(所以木棍都会用完的),这样,第i根小棍只要在第i-1..n-1中进行尝试即可。
剪枝:见代码
#include <iostream>
#include <stdio.h>
#include <algorithm>
using namespace std;
int n,sum,total,L;
struct node
{
int length; //木棒的长度
bool flag; //木棒是否被使用过
bool operator<(const node &t)const
{
return length>t.length;
}
}Sticks[100];
//cnt 已组成的木棒数目,len已经组成的长度,pos搜索的木棒的下标的位置
bool dfs(int cnt,int len,int pos)
{
if(cnt==total)
{
return true;
}
for (int i=pos; i<n; i++)
{
if(Sticks[i].flag)
{
continue;
}
if(len+Sticks[i].length==L)
{
Sticks[i].flag=true;
if(dfs(cnt+1, 0, 0)) //搜第cnt+1根木棒能否成功
{
return true;
}
Sticks[i].flag=false;
//剪枝, 第cnt+1根不能搜出来,则此时的方案(L长的木棍)作废
return false;
}
else if(len+Sticks[i].length<L)
{
Sticks[i].flag=true;
if(dfs(cnt,len+Sticks[i].length, i+1))//如果这里搜不出来 并且是搜第cnt根中的第一段 那么后面的剪枝就会生效,此方案不行,前面的组合作废
{
return true;
}
Sticks[i].flag=false;
}
//剪枝 针对第二个dfs退出时特殊的情况的剪枝
if(len==0)
{
return false;
}
//相同的木棍,上一个满足条件的找了但是没有找到,那么这个也一定找不到
while (Sticks[i].length==Sticks[i+1].length)
{
i++;
}
}
return false;
}
int main()
{
while (scanf("%d",&n) && n)
{
sum=0;
for (int i=0; i<n; i++)
{
scanf("%d",&Sticks[i].length);
Sticks[i].flag=false;
sum+=Sticks[i].length;
}
//将木棒按照长度从长到短的顺序排序
sort(Sticks, Sticks+n);
//从木棒的最长的那根开始搜索,因为最小的组合也会大于等于最长的那根
for (L=Sticks[0].length; L<=sum; L++)
{
//剪枝如果不能被整除说明不能组成整数根木棒,搜下一个
if(sum%L!=0)
{
continue;
}
total=sum/L; //木棒总数目
if(dfs(1, 0, 0))
{
printf("%d\n",L);
break;
}
}
}
return 0;
}