链接:https://ac.nowcoder.com/acm/problem/50243
来源:牛客网
时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 32768K,其他语言65536K
64bit IO Format: %lld
题目描述
乔治有一些同样长的小木棍,他把这些木棍随意砍成几段,直到每段的长都不超过50。现在,他想把小木棍拼接成原来的样子,但是却忘记了自己开始时有多少根木棍和它们的长度。给出每段小木棍的长度,编程帮他找出原始木棍的最小可能长度。
输入描述:
第一行为一个单独的整数N表示砍过以后的小木棍的总数。第二行为N个用空格隔开的正整数,表示N根小木棍的长度。
输出描述:
输出仅一行,表示要求的原始木棍的最小可能长度。
示例1
输入
9
5 2 1 5 2 1 5 2 1
输出
6
备注:
1<=N<=60
题目大意:
给出n个小木棍,你的任务是把他们拼接起来,还原原来的木棍,求拼接后每个小木棍的最小长度。
解题思路:
这个题注意一定要用搜索做而不是二分,之前做过一个拆分木棍的题是二分,因为那个题长度和拆分的数量有一个线性关系,而这个合并木棍并没有,我们用DFS+剪枝AC这个题,因为要求最小的长度,我们先将木棍排序,因为要求拼成的最小的长度,我们要从这些木棍最长的开始往上枚举,对每一个枚举的长度进行搜索,搜索的状态是(剩余木棍的个数,枚举的长度,当前拼成木棍剩余长度,和当前枚举的哪一根木棍)另外需要剪枝操作,剪枝有以下几点:
- 如果当前木棍是当去拼枚举长度的第一根,则需要剪掉,因为我们从最长的开始枚举,如果连第一个都拼不上,那么后面的肯定也拼不上。
- 如果当前木棍是当去拼枚举长度的最后一根,也需要剪掉,因为木棍是从大到小排,如果这个长度都不行,那后面的长度肯定比这个还小,肯定也拼不上,剪掉。
- 如果当前小木棍拼不上,那么和当前木棍长度相同的木棍肯定也拼不上,直接过滤掉和当前木棍长度相等的木棍即可。
AC代码:
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 65;
int a[N],n;
bool vis[N];
bool cmp(int a,int b) { return a>b; }
bool dfs(int num,int len,int rest,int now)
{
if(num==0&&rest==0)//当前剩余0个木棍且当前要拼的长度为0时代表可以拼成
return true;
if(rest==0)
rest=len,now=0;
for(int i=now;i<n;i++)
{
if(a[i]>rest)
continue;
if(!vis[i])
{
vis[i]=true;
if(dfs(num-1,len,rest-a[i],i+1))
return true;
vis[i]=false;
if(a[i]==rest||len==rest)//第一个木棍和最后一个木棍的时候剪掉
break;
while(a[i]==a[i+1])//过滤掉相同的木棍
i++;
}
}
return false;
}
int main()
{
int sum=0,s=0;
scanf("%d",&n);
memset(vis,false,sizeof vis);
for(int i=0;i<n;i++)
{
scanf("%d",&a[i]);
if(a[i]<=50)
sum+=a[i];
else
s++;
}
n-=s;
sort(a,a+n,cmp);
for(int i=a[0];i<=sum;i++)//从最大的开始枚举
{
if(sum%i==0)
if(dfs(n,i,0,0))
{
printf("%d
",i);
break;
}
}
return 0;
}