题意
- 给定n根短棍,将其拼成若干等长的长棍,求能拼成的长棍的最短长度是多少
思路
- 枚举长棍的长度,深搜判断能否拼成(将每一个棍尝试摆上去,如果摆上去不大于枚举长度就深搜下一层,超过的就跳过,最终判断所有棍用完的时候,最后一根长棍是否刚好拼完)
- 优化一:对于枚举,枚举区间为最长的棍的长度到所有棍的长度和,且不能被总和整除的直接跳过
- 深搜优化一:对于所有短棍,先放短的再放长的,剪掉短的用完后长的无法拼成的情况
- 深搜优化二:对于一根短棍,如果回溯回到它(它放的有问题),说明则跳过所有和它等长的短棍
- 深搜优化三:如果回溯到的短棍是一根长棍的结尾,则换成比他短的一定不会更优,所以说明换成短的没有意义,换成长的不合法,应当直接再次回溯,重新枚举倒数第二根;同样地,如果回溯到的是开头,说明上一根有问题/这个长度不成立
- 深搜优化四:对于组成长棍的短棍,越往后约束条件越严,所以后面部分选取短棍时,一定不会选择比上一根短棍更长(序号更小)的短棍,除非是新开了一根长棍,故开last来减少深搜内的枚举次数
- 以上优化少任何一个都无法AC
AC代码
#include<bits/stdc++.h>
using namespace std;
int a[100],vis[100],n;
bool dfs(int len,int rg,int rl,int last){
if(rg==0&&rl==0) return true;
if(rl==0) {rl=len;last=-1;}
for(int i=last+1;i<n;i++){
if(vis[i]) continue;
if(a[i]>rl) continue;
vis[i]=1;
if(dfs(len,rg-1,rl-a[i],i)){
// cout<<rl-a[i]<<' '<<i<<' '<<a[i]<<'\n';
return true;
}
vis[i]=0;
if(a[i]==rl||len==rl)return false;
while(a[i]==a[i+1])i++;
}
return false;
}
bool cmp(int a,int b){return a>b;}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin>>n;
int sum=0;
for(int i=0;i<n;i++){
cin>>a[i];
sum+=a[i];
}
sort(a,a+n,cmp);
for(int i=a[0];i<=sum;i++){
if(sum%i!=0)continue;
memset(vis,0,sizeof(vis));
if(dfs(i,n,i,-1)==1){
cout<<i<<'\n';
break;
}
}
return 0;
}