时间限制: 1 Sec 内存限制: 128 MB
题目描述
乔治有一些同样长的小木棍,他把这些木棍随意砍成几段,直到每段的长都不超过50。
现在,他想把小木棍拼接成原来的样子,但是却忘记了自己开始时有多少根木棍和它们的长度。
给出每段小木棍的长度,编程帮他找出原始木棍的最小可能长度。
输入
输入文件共有二行。
第一行为一个单独的整数N表示砍过以后的小木棍的总数,其中N≤60。
第二行为N个用空个隔开的正整数,表示N根小木棍的长度。
输出
输出文件仅一行,表示要求的原始木棍的最小可能长度。
样例输入
9 5 2 1 5 2 1 5 2 1
样例输出
6
题解:
可以依次枚举所有可能的长度len,用深搜判断是否能用截断后所有的小木棍拼出整数个len,找出最小的len
最优性剪枝:
1、设所有木棍的长度和是sum,那么原长度一定能够被sum整除,不然就没法拼了
2、木棍原来的长度一定大于等于所有木棍中最长的那根
可行性剪枝:
3、短小的木棍可以更灵活,可以对输入的木根从大到小排序
4、当用木棍i拼合时,可以从第i+1后的木棍开始搜索,因为根据优化1,前面的木棍已经用过了
5、用当前最长的木棍开始搜索,如果拼不出当前设定的原木棍的长度,则直接返回,换一个原始木根长度
6、相同长度的木棍不要搜索多次,用当前木棍的长度搜下去得不到结果时,用一支同样长度的还是得不到结果,所以,可以提前返回
7、判断搜到的几根木棍组成的长度是否大于原始长度len,如果大于,没必要搜下去,提前返回
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
int n,sum=0,a[60],len,m,temp,minn=0,used[60];
bool cmp(int x,int y)
{
return x>y;
}
void read()
{
cin>>n;
for (int i=0;i<n;i++)
{
cin>>a[i];
sum+=a[i];
}
sort(a,a+n,cmp); //剪枝3,从大到小排序,长木棍的灵活性差,先排长木棍
}
void dfs(int k,int last,int rest) //第k根木棍,last为第k根上一节木棍编号,rest为第k根木棍还需要的长度
{
if (k==m) {temp=1;return;}
if (rest==0) //开始下一根木棍
{
int i;
for (i=0;i<n;i++) //剪枝4
if (!used[i]) {used[i]=1;break;} //找到没有用过最长的木棍作为第一节
dfs(k+1,i,len-a[i]);
}
for (int i=last+1;i<n;i++) //剪枝5、7,因为已经排好序了,所以它的下一个不大于它的长度
{
int j;
if (!used[i]&&rest>=a[i]) //木棍未被访问过,且比需要的木棍长度小
{
used[i]=1;
dfs(k,i,rest-a[i]);
used[i]=0; //回溯
j=i; //记录当前的木棍的编号
while (i<n-1&&a[i]==a[j]) i++; //剪枝6,防止相同长度的木棍搜索多次
if (i==n-1)return ;
}
}
}
void solve()
{
for (int i=a[0];i<=sum;i++) //剪枝2
if (sum%i==0) //剪枝1,sum能被原木棍的长度整除
{
memset(used,0,sizeof(used));
len=i;
used[0]=1;
temp=0; //记录有没有得到答案
m=sum/i; //木棍的根数
dfs(1,0,len-a[0]);
if (temp) {cout<<len<<endl;break;}
}
}
int main ()
{
read();
solve();
return 0;
}