【题意简述】: n根长度不同的小棍,拼成若干根长棍,要这些长棍的长度相等,并且小棍刚刚好都用完,问能拼成的长棍的最短长度是多少?
【思路】: 积累!!
首先 ,把小棍按长度,从大到小排序(为了进行贪心选择),并计算这些小棍的总长度,拼成的长棍的长度从最长
的小棍开始进行搜索,如果小棍的总长度能整除该长棍的长度,则可能完成拼凑。
进而 ,可以把问题花间成n根小棍,长度已知,拼成 num 根长度为 sum/num 的长棍,若能整除,便输出1,结束。 否则增加木棍的长度,继续搜索。最坏的情况下,所有小木棍拼在一起为一根长木棍!所以,无论怎样搜索总能
成功。
由于过程中要确定某根小棍是否能被成功接收,它就得提前预知加入这根小棍后其他的小木棍能不能匹配成功,
就要在遍历某个小木棍的时候,对其后的木棍进行递归搜索(深搜的特点),若能匹配成功,则标记当前小木棍为
用过,可以直接返回(试探成功)
如不能匹配,说明此木棍目前不可使用,将标记取消,待下一次搜索的时候再用。若当前木棍不可用,那么与这
根小木棍长度相同的木棍自然也不可使用,直接将其跳过(剪枝)。
// 132k 0ms
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define maxn 70
int n,sum,aim,num,a[maxn];
bool used[maxn];
int cmp(int x,int y)
{
if(x>y) return 1;
else return 0;
}
bool dfs(int sticks,int len,int pos)
{
//Stick表示当前组合好的木棍数,len表示已经有的长度,pos表示搜索到第几根
int i;
bool sign = (len == 0 ? true:false);
if(sticks == num) //全部完成组合,搜索成功推出函数
return true;
for(i = pos + 1;i < n; i++)
{
if(used[i]) continue; //棍子已经用过就不再用了
if(len + a[i] == aim) //正好完成组合
{
used[i] = true; //标记第i根棍子被使用
if(dfs(sticks + 1, 0, -1)) //进入下一层搜索
return true;
used[i] = false; //若下一层搜索失败则证明第i根不应使用,
return false; //所以要置false
}
else if(len + a[i] < aim) //长度不够
{
used[i] = true; //标记第i根棍子被使用
if(dfs(sticks,len + a[i],i)) //进入下一层搜索,已组合好的棍子长度变为len + a[i]
return true;
used[i] = false;
if(sign) return false;
while(a[i] == a[i+1]) i++; //剪枝!
}
}
return false;
}
int main()
{
while(scanf("%d",&n)==1)
{
if(n==0) break;
sum=0;
for(int i=0;i<n;i++)
{
scanf("%d",&a[i]);
sum+=a[i]; //所有木棍的长度和是结果的上限
}
sort(a,a+n,cmp); //对sticks的长度降序排列
for(aim=a[0];aim<=sum;aim++) //枚举每一种可能的木棍长度,最长为
//sum,最短为a[0];
if(sum%aim==0){
num = sum / aim; //木棍的个数
memset(used,false,sizeof(used));//注意每次都要初始化!
if(dfs(1,0,-1)){
printf("%d\n",aim);
break;
}
}
}
return 0;
}