AcWing 167. 木棒

原题链接

考察:dfs+剪枝

思路:

       写dfs先定搜索顺序,很明显我们需要枚举和,一共64个木棒,如果暴力枚举是264,必然TLE,因此在枚举和时就需要优化.可以发现如果这些木棒能拼成等长的大木棍,那么所有木棒的和一定是大木棍的倍数.由此我们只需要枚举sum的约数.定下搜索顺序后,就是将木棒分成等长的x组的问题.

       再考虑剪枝,首先要选分支少的,很明显拼接首先选长的,所有将所有木棒进行从大到小排序.

       其次要去除冗余情况,很明显这里是组合性枚举.还有如果当前长度为s的配对失败,后面长度为s的也不用配对.

       最优性剪枝,将答案从小到大枚举.

       最后两个剪枝很难想:

  1. 当我们拼接大木棍的第一个木棒不成功时(没有能与该木棒配对的,或者在后面难以配对),直接返回上一个分支.因为木棒是从大到小开始枚举的.

        2.当我们拼接大木棍的最后一个木棒不成功时,也是直接返回上一个分支,不需要枚举.这里的不成功有两种情况:

  • 没有能与正在拼接的木棍配对的,此方案一定错误.
  • 有能配对的,此方案不成功说明后面配对失败.我们假设当前木棒s配对失败,根据枚举我们会以>1的小木棒(和为s)代替木棒s.但是此替换与直接放s没有区别,所以直接return
 1 #include <iostream>
 2 #include <cstring>
 3 #include <cstdio>
 4 #include <algorithm>
 5 using namespace std;
 6 const int N = 70;
 7 int n,a[N],cnt,ans,m;
 8 bool st[N];
 9 struct cmp{
10     bool operator()(int x,int y)
11     {
12         return x>y;
13     }
14 };
15 bool dfs(int p,int len,int start)
16 {
17     if(p>=cnt) return 1;
18     if(len==ans) return dfs(p+1,0,1);
19     int fall = 0;
20     for(int i=start;i<=m;i++)
21     {
22         if(!st[i]&&a[i]+len<=ans&&fall!=a[i])
23         {
24             st[i] = 1;
25             if(dfs(p,len+a[i],i+1)) return 1;
26             st[i] = 0;
27             fall = a[i];
28             if(!len||len+a[i]==ans) return 0;
29         }
30     }
31     return 0;
32 }
33 int main()
34 {
35     while(scanf("%d",&n)!=EOF&&n)
36     {
37         int sum = 0,x; m = 0;
38         for(int i=1;i<=n;i++)
39         {
40             scanf("%d",&x);
41             if(x>50) continue;
42             a[++m] = x;
43             sum+=x;
44             st[i] = 0;
45         }
46         sort(a+1,a+m+1,cmp());
47         ans = a[1];
48         while(ans<=sum)
49         {
50             if(sum%ans==0)
51             {
52                 cnt = sum/ans;
53                 if(dfs(0,0,1)) {printf("%d\n",ans);break;}
54             }
55             ans++;
56         }
57     }
58     return 0;
59 }

 

posted @ 2021-03-09 15:10  acmloser  阅读(78)  评论(0编辑  收藏  举报