题目链接:http://poj.org/problem?id=1011
剪枝策略:
1.优化搜索顺序:
将每一段木棒的长度从大到小排序。
2.排除等效冗余:
(1)设两根木棒长度分别为x,y,且x<y,那么先拼上x再拼y和先拼y再拼x是等效的,只需要搜索其中的一种。那么可以限制选的顺序为递减的。
(2)对于当前的木棒,可以记录最近一次尝试拼入的木棍长度。如果分支搜索失败回溯,不再向该木棒中加入其他长度相同的木棍(因为必定也会失败)。
(3)如果在当前原始木棒尝试拼入第一根木棍的递归就返回失败,那么直接判定整个分支失败,立即回溯。因为在拼入这根木棒前,所有的原始木棒都是空的。木棒在拼当前木棒中失败,在拼其他木棒中一样会失败。
(4)如果在当前原始木棒中拼入一根木棍后,木棒恰好被拼接完整,并且接下来拼接剩余原始木棒的递归分支失败,那么可以判定整个分支失败。(可以用贪心来解释)
AC代码:
1 #include<cstdio> 2 #include<iostream> 3 #include<algorithm> 4 #include<cstring> 5 using namespace std; 6 const int N=80; 7 int n,len,cnt,val,sum; 8 int vis[N],a[N]; 9 bool cmp(int x,int y){ 10 return x>y; 11 } 12 bool DFS(int stick,int now,int last){ 13 if(stick>cnt) return 1; 14 if(now==len) return DFS(stick+1,0,1); 15 int fail=0; 16 for(int i=last;i<=n;i++){ 17 if(!vis[i]&&now+a[i]<=len&&fail!=a[i]){ 18 vis[i]=1; 19 if(DFS(stick,now+a[i],i+1)) return 1; 20 fail=a[i]; 21 vis[i]=0; 22 if(now==0||now+a[i]==len) return 0; 23 } 24 } 25 return 0; 26 } 27 int main(){ 28 while(~scanf("%d",&n)&&n){ 29 sum=val=0; 30 for(int i=1;i<=n;i++) scanf("%d",&a[i]),val=max(val,a[i]),sum+=a[i]; 31 sort(a+1,a+n+1,cmp); 32 for(len=val;len<=sum;len++){ 33 if(sum%len) continue; 34 cnt=sum/len; 35 memset(vis,0,sizeof(vis)); 36 if(DFS(1,0,1)) break; 37 } 38 printf("%d ",len); 39 } 40 return 0; 41 }