暴搜的思路并不难想到,主要难点是各种优化、各种剪枝:
预先处理出所有木棍的总长度,且保证枚举答案的值能被总长度整除。
每根木棍的长度可用桶来存储,并且预先处理出最长的和最短的木棍的长度,搜索时从最大长度到最小长度递减枚举。
若拼接当前木棍时已用了一根长为X的木棍,则dfs时从长度X开始搜索。
若某组拼接不成立,且此时 已拼接的长度为0 或 当前已拼接的长度与刚才枚举的长度之和为最终枚举的答案 时,则可直接跳出循环,因为此时继续枚举其它更小的值时,显然可能情况更少,且同样凑不完。
注意常数不要太大。
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int n,nn[70],sum=0,mid2=0,mii=0,md,flag=0,mxx,mnn=2e9;
int cnt=0,x;
/*
nn数组表示当前长度的木棍的个数 sum表示全部木棍长度
mid2表示原本有多少根木棍
*/
int max(int a,int b){return a>b?a:b;}
int min(int a,int b){return a>b?b:a;}
void checkdfs(int ss,int sm,int p){//(一共有多少根大木棍,和为多少,从长度为多少的木棍开始搜索)
if(ss==0){printf("%d",md);exit(0);}
if(sm==md){checkdfs(ss-1,0,mxx);return;}
for(int i=p;i>=mnn;i--){
if(nn[i]&&(i+sm<=md)){
nn[i]--;
checkdfs(ss,sm+i,i);
nn[i]++;
if(sm==0||sm+i==md)break;
}
}
return;
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&x);
if(x>50)continue;
nn[x]++;
cnt++;
sum+=x;
mxx=max(mxx,x);
mnn=min(mnn,x);
}
//mii=sum;
for(int i=cnt;i>=2;i--){//从 初始化棍数最多 开始找 因为棍数越多,每根长度越小
if(sum%i==0){
mid2=i;
md=sum/i;
//printf("mid2:%d\n md:%d",mid2,md);
//flag=0;
checkdfs(mid2,0,mxx);
//if(flag){mii=min(mii,md);}
}
}
/*for(int i=mxx;i<=sum>>1;i++){
if(sum%i==0){
md=i;
flag=0;
checkdfs(sum/i,0,i,50);
//if(flag)mii=min(mii,i);
}
}*/
printf("%d",sum);//最后都没输出,则输出 所有小木棒的总和
return 0;
}