POJ_1011
做完这个题目,让我不仅学到了一些别人剪枝的策略,更重要的是让我意识到了在搜索中剪枝的重要性。
所谓剪枝,其实就是对程序的优化,尽可能地避免程序执行无用的操作。就这个题目而言,程序的优化主要有以下几个方面:
①我们在拼木棒之前应该对木棒最长到短进行排序,这样方便我们后面挑选木棒的操作,因为如果前面先拼短木棒的话,很有可能后面任何一根木棒都不能填补剩余的长度,而先拼长木棒就可以在本来有解的情况下避免出现这种问题。
②枚举拼好的木棒的长度时,枚举区间应在max(len)与sum/2之间,如果在这个区间内还没有找到解,那么最后的解只能是sum。
③枚举的拼好的木棒的长度必须能够整除所有木棒长度和。
(为了后续描述方便,我们假定现在使用的dfs函数为int dfs(int rest,int complete,int start),其中rest为拼当前木棒时还缺少的长度,complete为已经拼好的木棒数,start为从哪根木棒开始搜索待拼的木棒。同时用d表示枚举的拼好的木棒的长度,cn表示总共可以拼成的木棒的个数。)
④搜索过程中,如果这个时候rest==d,也就是说刚刚开始拼一根新的木棒,假如这时把没有用过的最长的一根木棒拼上去,但最后无解的话,那么便不需要去尝试把后面的木棒拼上去了,因为后面不管第几次拼,总要用到这根木棒的,所以出现这种情况,不管后面再尝试拼哪根,最后都一定是无解的。
⑤搜索过程中,如果当前木棒和前一根木棒是一样的长度,而前一根木棒没有用过的话,那么这根木棒也一定不使用的,所以直接跳过这根木棒就可以了。
⑥搜索过程中,如果遇到一根木棒,它的长度恰好等于rest,但把它拼上去之后无解,那么也便不需要再尝试把后面的木棒拼上去了。
剪枝确实是门艺术啊,只有勤于思考,善于发现才能把剪枝做好,以后遇到这样的题我一定要更多地独立思考,这样才能更好得锻炼自己的思维能力!
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
int state[70],st[70],n,sum,d,cn,min,max;
int cmp(const void *_p,const void *_q)
{
int *p=(int *)_p;
int *q=(int *)_q;
return *q-*p;
}
int dfs(int rest,int complete,int start)
{
int i;
if(rest==0)
{
complete++;
if(complete==cn)
return 1;
for(i=0;st[i]!=0;i++);
st[i]=1;
if(dfs(d-state[i],complete,i+1))
return 1;
st[i]=0;
}
else
{
for(i=start;i<n;i++)
{
if(i>0&&state[i]==state[i-1]&&!st[i-1])
continue;
if(!st[i]&&rest>=state[i])
{
st[i]=1;
if(dfs(rest-state[i],complete,i+1))
return 1;
st[i]=0;
if(state[i]==rest)
break;
}
}
}
return 0;
}
int main()
{
int i,j,k,ok;
while(1)
{
scanf("%d",&n);
if(n==0)
break;
sum=0;
for(i=0;i<n;i++)
{
scanf("%d",&k);
sum+=k;
state[i]=k;
}
qsort(state,n,sizeof(state[0]),cmp);
memset(st,0,sizeof(st));
ok=0;
for(d=state[0];d<=sum/2;d++)
if(sum%d==0)
{
cn=sum/d;
if(dfs(d,0,0))
{
ok=1;
printf("%d\n",d);
break;
}
}
if(!ok)
printf("%d\n",sum);
}
return 0;
}