POJ 1011 Sticks(DFS+剪枝)

题目链接: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\n",len);
39     }
40     return 0;
41 }
AC代码

 

posted @ 2020-11-04 19:34  dfydn  阅读(54)  评论(0编辑  收藏  举报