UVA_307
相当于重温了一下POJ 1011这个题目,剪枝在这个题目中显得尤为重要。
对于这道题而言,剪枝的策略一般有下面6个:
①先将木棒长度从大到小进行排序,这样便于后面的选择和操作,是后面一些剪枝算法的前提。
②在枚举原木棒长度时,枚举的范围为max与sum/2之间,如果这个区间内没有找到合适的长度,那么最后原木棒的长度只能是sum。
③枚举的原木棒的长度只能是sum的约数。
④在深搜过程中,如果当前木棒和前一个木棒的长度是一样的,但是前一个木棒没有被选上,那么这个木棒也一定不会被选上。
⑤在深搜过程中,如果当前是在拼一根新木棒的第一截,但如果把可用的最长的一根木棒用上后不能拼成功的话,那么就不用再试后面的木棒了,肯定是前面拼的过程出了问题。
⑥在深搜过程中,如果当前可用的木棒恰好能补上一根原木棒的最后一截,但用它补上之后却不能用剩下的木棒完成后续的任务,那么也不用再试后面的木棒了,肯定是前面拼的过程出了问题。
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
int sum,N,n,L,a[100],vis[100];
int cmp(const void *_p,const void *_q)
{
int *p=(int *)_p;
int *q=(int *)_q;
return *q-*p;
}
int dfs(cur,complete,len)
{
int i;
if(len==L)
{
complete++;
if(complete==N)
return 1;
else
{
for(cur=0;vis[cur];cur++);
vis[cur]=1;
if(dfs(cur+1,complete,a[cur]))
return 1;
vis[cur]=0;
}
}
else
{
for(i=cur;i<n;i++)
if(!vis[i]&&a[i]<=L-len)
{
if(i!=0&&a[i]==a[i-1]&&!vis[i-1])
continue;
vis[i]=1;
if(dfs(i+1,complete,len+a[i]))
return 1;
vis[i]=0;
if(a[i]==L-len)
return 0;
}
}
return 0;
}
int main()
{
int i,j,k,max;
while(1)
{
scanf("%d",&n);
if(n==0)
break;
sum=max=0;
for(i=0;i<n;i++)
{
scanf("%d",&a[i]);
if(a[i]>max)
max=a[i];
sum+=a[i];
}
qsort(a,n,sizeof(a[0]),cmp);
memset(vis,0,sizeof(vis));
for(L=max;L<=sum/2;L++)
{
if(sum%L!=0)
continue;
N=sum/L;
if(dfs(0,0,0))
break;
}
if(L>sum/2)
printf("%d\n",sum);
else
printf("%d\n",L);
}
return 0;
}