木棍
题目描述
小Z拿出长度相同的一些原始棍子并进行随意的切割,直到所有切割出的小棍子的长度都不大于50个单位。现在他想把棍子恢复成原来的状态,但是他忘记了原来有多少根棍子也忘记了原来棍子的长度是多少。
请你帮助他编写一个程序,计算出这些棍子最小的原始长度。所有棍子的长度都是大于0的整数。
输入格式
输入包含多组数据,对于每组数据包含两行。
第一行,切割完成的木棍的数量,最多64根
第二行,用空格隔开的整数,分别表示这些棍子的长度
当输入为0时,表示输入结束
输出格式
对于每组数据输出一行,一个整数表示棍子最小的原始长度
输入输出样列
输入样例1:
9
5 2 1 5 2 1 5 2 1
4
1 2 3 4
0
输出样例1:
6
5
【耗时限制】1000ms 【内存限制】128MB
解题思路:
设n根小木棍的长度分别为L1,L2,L3……Ln;则原始木棍的长度为:len∑[max(Li),sum(Li)];
枚举:从小到大枚举原始木棍的可能长度len,判断len是否满足要求。
如何判断len是否满足要求?
- 木棍总长度sum必须能被len整除,原始小木棍的数量num=sum/len;
- 找到一种使用n根小木棍拼凑出num个长度为len的原始木棍方法。
搜索:找到一种使用n根小木棍拼凑出num个长度为len的原始木棍的方法。
●相当于向num个盘子里面放棍子,保证每个盘子里棍子长度的累加和都是len。
●到达目的地:
●可选范围:从1到第n根中,所有还未使用过的小棍子;
●可行性剪枝:拼凑第cur根原始棍子时,已拼凑的长度+待选棍子长度>num则剪枝;
●时间复杂度:O(n!)
#include<bits/stdc++.h>
using namespace std;
const int N=80;
int n,sticks[N],num,len,tot,maxlen;
bool vis[N];
bool dfs(int cur,int sum){
if(cur>num)return true;
if(sum==len)return dfs(cur+1,0);
for(int i=1;i<=n;i++){
if(!vis[i]&&sum+sticks[i]<=len){
vis[i]=true;
if(dfs(cur,sum+sticks[i]))return true;
vis[i]=false;
}
}
return false;
}
int main()
{
while(cin>>n&&n){
tot=maxlen=0;
memset(vis,0,sizeof(vis));
for(int i=1;i<=n;i++){
cin>>sticks[i];
maxlen=max(maxlen,sticks[i]);
tot+=sticks[i];
}
}
for(len=maxlen;len<=tot;len++){
if(tot%len!=0)continue;
num=tot/len;
if(dfs(1,0))break;
cout<<len<<endl;
}
return 0;
}
解决重复搜索问题。
在搜索拼接原始木根时,同一组小木棍会重复搜索 :
例:len=6时,在拼接第1根原始木棍时,尝试完(1)(3)后还会尝试(3)(1);
在搜索拼接原始木棍时,不同的原始木棍会重复使用相同的小木棍:
例:len=6时,第1根棍子选(1)(3),第2根棍子选(4)(6);反之亦可;
排除等效赘余:
用来拼接同一根原始木棍的小木棍,按照编号严格递增:即(1)(3)可以,(3)(1)不可以。
用来拼接第cur根原始木棍的第1根小木棍的编号严格大于cur-1根原始木棍的第1根小木棍,(1)(3),(4)(6)可以,(4)(6),(1)(3)不可以。
#include<bits/stdc++.h>
using