题意
有 n n n 个长度为 a i a_{i} ai 小木棒,要将其拼成若干个长度相等的小木棍,问拼成的小木棍最小长度为多少。
思路
考虑深搜。每次传入三个参数,分别是小木棍的数量 u u u,当前小木棍匹配到的小木棒数量 c n t cnt cnt,以及当前匹配的小木棒编号 c u r cur cur。
普通思路就是每次从小到大枚举小木棍的长度,然后爆搜,看每次小木棒的总长度是否可以划分成若干个长度为此时枚举的长度,因为是从小到大,所以第一个合法的可能长度就是最优解。
可这样一定会 T L E TLE TLE,所以我们考虑剪枝。
1. 朴素优化
我们知道,所有的小木棒长度一定可以被小木棍的长度整除,所以当我们需要搜索的时候只需要去搜索所有小木棒的长度的约数就好啦。
2. 搜索顺序优化
比较长的木棍一定比比较短的木棍的用处少,所以我们应该优先处理长的木棍,考虑将小木棒的长度从大到小排序,让比较短的小木棍能够更加灵活的运用。
3. 避免冗余重复
- 结论:如果当前放入长度为
i
i
i 的小木棒匹配失败了,那么如果将其替换成一个长度也为
i
i
i 的小木棒,也会匹配失败。
证明:考虑反证,如果将其替换成一个长度也为 i i i 的小木棒且匹配成功,那么这时长度为 i i i 的就只能放入下一组拼合的小木棍。因为它们俩的长度相等,所以可以互相交换。这时 i i i 回到了之前匹配失败的位置,但却匹配成功了,自相矛盾,所以会匹配失败。 - 结论:如果将长度为
i
i
i 的小木棒放到了第一个位置匹配失败了,那么从
i
i
i 放下之前绝对需要回溯修改之前的某些木棍。
证明:在将小木棒放入小木棍之中之前,小木棍中什么都没放。如果放下它后成功了,那么说明它就是一个必不可少的小木棒,自己会成一个小木棍或与其他小木棒拼接成一个小木棍。但是它失败了,说明现在这根木棍不可以用,前面的木棍一定是有用错的。 - 结论:如果将长度为
i
i
i 的小木棒放到了最后一个位置,和一些小木棒拼成了一个小木棍却匹配失败了,那么从
i
i
i 放下之前绝对需要回溯修改之前的某些木棍。
证明:在小木棒放入小木棍之后小木棍拼成了一个小木棍,说明这时绝对是最优的方法(如果用了多个小木棍拼就不满足第一个剪枝的规定,并且后面长度较小的小木棍将不灵活),但是它却失败了,说明问题一定出在前面,所以回溯进行修改。
代码
#include<bits/stdc++.h>
using namespace std;
int a[1000001];
bool vis[1000001];
int n,sum,ans;
bool cmp(int a,int b){return a>b;}
bool dfs(int u,int cnt,int cur)
{
if(u*ans==sum) return 1;
if(cnt==ans) return dfs(u+1,0,0);
for(int i=cur;i<=n;i++)
if(!vis[i]&&a[i]+cnt<=ans)
{
vis[i]=1;
if(dfs(u,cnt+a[i],i+1))
return 1;
vis[i]=0;
if(!cnt||a[i]+cnt==ans)
return 0;
int j=i;
while(j<=n&&a[i]==a[j])
i=j,j++;
}
return 0;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]),sum+=a[i];
sort(a+1,a+n+1,cmp);
while(++ans)
if(!(sum%ans)&&dfs(0,0,0))
{printf("%d",ans);break;}
return 0;
}