当年林会东给我们的爆炸J题,好像用了一下午优化没过,最后超的题解,还是总结一下。
3个剪枝。
1.从大到小排序,那么错误的组合方式就会先排掉。
2.每次选小棒组合按顺序选
3.若上次的长度一样且没有被选,那么当前这根跳过。
贴一发我高一在网上抄的源代码233
#include <stdio.h>
#include <string.h>
const int Max = 65;
int n, len, stick[65],i;//表示n根小棒
int flag=0;//完成的标志
int vis[65];//表示stick[i]表示是否使用过
void kuaipai(int a[],int l,int r)
{
int i,j,mid,t;
i=l;j=r;
mid=a[(i+j)/2];
while(i<=j)
{
while(a[i]>mid) i++;
while(a[j]<mid) j--;
if(i<=j)
{
t=a[i];a[i]=a[j];a[j]=t;
i++;j--;
}
}
if(i<r) kuaipai(a,i,r);
if(j>l) kuaipai(a,l,j);
}
void dfs(int dep, int now_len, int u)
{// dep为当前已被用过的小棒数,u为当前要处理的小棒下标。
if(flag) return;//一旦完成,flag为true,不在做下去。输出len即可
if(now_len == 0)
{ // 当前长度为0,寻找没用过的最长小棒。
int k = 0;
while(vis[k]) k ++; // 寻找第一个没用的最长小棒。
vis[k] = 1; //找后用之
dfs(dep + 1, stick[k], k + 1); //用多的数++,当前长度now_len=刚选的那根小棒,处理的为当前的小棒的下一根
vis[k] = 0; //
return;
}
if(now_len == len){ // 当前长度为len,即又拼凑成了一根原棒。
if(dep == n) flag = 1; // 完成的标志。flag一旦==true,不在做下去,直接输出!
else dfs(dep, 0, 0); //若还有小棒剩余,继续深度搜
return;
}
for(i=u;i<n;i++)//now_len在0到len之间:最主要的情况
//搜索之后的小棒,now_len+=stick[i]
if(!vis[i] && now_len + stick[i] <= len)
{
if(!vis[i-1] && stick[i] == stick[i-1])
continue; // 如果与上一根相等且上一个没被用过(就是上一个不行),不搜了,这个必然也不行。重要一剪
vis[i] = 1;//与上一个不同,用之
dfs(dep + 1, now_len + stick[i], i + 1);
//处理之后的
vis[i] = 0;
}
}//end of dfs()
int main()
{
int i;
while(scanf("%d",&n) && n!=0)
{
int sum=0;
flag=0;
for(i=0;i<n;i++)
{
scanf("%d",&stick[i]);
sum+= stick[i];
}
kuaipai(stick,0,n-1);
for(len = stick[0]; len < sum; len ++)
if(sum % len == 0)
{ // 枚举能被sum整除的长度。
memset(vis, 0, sizeof(vis));
dfs(0, 0, 0);
if(flag) break;
}
printf("%d\n", len);
}//end while
return 0;
}