题目大意:
已知我们有若干一样长的木棍,现在我们任意切,切完后 每段长度小于50,问,假若我们把这些小木棍拼为原来的木棍,那么可以得到的最短长度的原始木棍有多短。
解题思路:
首先,我们明确搜索的木棍范围是[maxl,sigmal/2],其中maxl为所有木棍中最长的长度,sigmal是所有木棍的长度求和。若在指定范围里面搜索不到可拼接方案,我们认为最优长度为sigmal。
我们每次选取多长,选取多少个木棍都是很难确定的,所以我们需要用到dfs搜索,dfs搜索是对搜索空间不明确时候使用的。
这里我们需要用到暴搜,我们搜索的范围是长度, dfs(l , len, idx)
其中l表示我们已经拼了几根,用于给递归一个结束条件。len表示我们已经拼接了多长,用于告诉我们是否可以拼接下一根,以及用于剪枝,idx表明我们从什么长度开始枚举,表明我们需要从这个范围以后开始枚举,让我们不要重复枚举。
在这个问题有三个剪枝可以优化:
首先我们选取木棒时,需要从大到小开始枚举,我们优先选长的木棒。
假若我们是第一根开始枚举,若最长那根都不行,我们就可以退出,因为往后搜索,它的搜索范围更窄。
假若我们是选择的这根加上原始长度等于目标长度,那么我们可以退出搜索。
#include <bits/stdc++.h>
using namespace std;
const int MAXN=70;
int tmt[MAXN];
int target;
int tot;
int maxl,minl;
void dfs(int no,int nolen,int idx){
if(no==tot/target){
cout<<target<<endl;
exit(0);
}
if(nolen==target){
dfs(no+1,0,maxl);
return ;
}
for(int i=idx;i>=minl;i--){
if(tmt[i]== 0 || nolen+i>target)continue;
tmt[i]--;
dfs(no,nolen+i,i);
tmt[i]++;
if(nolen==0 || nolen+i==target)break;
}
}
int main(){
int n;cin>>n;
maxl=-1;
minl=55;
for(int i=0;i<n;i++){
int t;cin>>t;
if(t>50)continue;
tot+=t;
tmt[t]++;
maxl=max(maxl,t);
minl=min(minl,t);
}
for(int len=maxl;len<=tot>>1;len++){
if(tot%len!=0)continue;
target=len;
dfs(0,0,maxl);
}
cout<<tot<<endl;
return 0;
}