Description
乔治有一些同样长的小木棍,他把这些木棍随意砍成几段,直到每段的长都不超过50。
现在,他想把小木棍拼接成原来的样子,但是却忘记了自己开始时有多少根木棍和它们的长度。
给出每段小木棍的长度,编程帮他找出原始木棍的最小可能长度。
Input
共有二行。
第一行为一个单独的整数N表示砍过以后的小木棍的总数,其中N≤60,第二行为N个用空格隔开的正整数,表示N根小木棍的长度。
Output
仅一行,表示要求的原始木棍的最小可能长度。
Sample Input
9
5 2 1 5 2 1 5 2 1
Sample Output
6
Hint
把大于50的略去不管。
Source
搜索
Solution
dfs+优秀的剪枝
搜索就是枚举长木棍的长度(范围是从最长的小木棍长到小木棍总长/2)符不符合要求,这道题中剪枝十分重要; 1.木棍的长度从大到小排序,因为经验告诉我们越长的木棍灵活性越差,所以优先放置长一点的木棍。2.如果当前长度的木棍无法和后面的木棍组成合法的解,那么和它同样长度的木棍一样不能,因此我们可以跳过和它长度相同的木棍。 3. 如果组成原始木棍的第一根木棍无法得出合法解,那么就直接返回不必搜索了,因为以后也不可能得出合法解。
code
#include <cstdio>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <vector>
#define N 61
using namespace std;
int st[N],vis[N];
int n,sum,stsum,maxst,ans;
bool flag;
void dfs(int GoalLength,int DoneSum,int DoneLength,int num)//目标木棍长,已经凑出来的木棍数量,正在凑的这一根木棍已经凑出来的数量,num是小木棍在数组中排序后的长度,为了方便剪枝1
{
if(DoneSum==stsum)
{
flag=true;ans=GoalLength;return;
}
if(flag) return;
for(int i=num;i>=1;i--)
if(!vis[i]&&st[i]+DoneLength<=GoalLength)
{
vis[i]=1;
DoneLength+=st[i];
if(DoneLength==GoalLength) dfs(GoalLength,DoneSum+1,0,st[0]);//凑出了一根,凑下一根,注意这里num要改成st[0]
else dfs(GoalLength,DoneSum,DoneLength,i-1);//没凑出一根,继续凑,从i-1开始是因为如果长度为st[i]都不能放进去的话,那么比i大的肯定也放不进去
if(flag) return;
vis[i]=0;
DoneLength-=st[i];
int bobo=st[i];
if(!DoneLength) return;//剪枝3
while(i&&st[i]==bobo) i--;//剪枝2
i++;
}
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
int u;
scanf("%d",&u);
if(u>50) continue;//这里注意一定要过滤掉长度大于50的木棍
st[++st[0]]=u;//用st[0]计符合要求的小木棍数
sum+=u;
maxst=max(maxst,u);
}
int i;
sort(st+1,st+st[0]+1);
for(i=maxst;i<=sum/2;i++)//从最长的一根小木棍的长度开始枚举长度,最大为sum/2,即这些小木棍最少是从两根长木棍切出来的
{
memset(vis,false,sizeof(vis));
if(sum%i) continue;//如果不能整除,那么肯定拼不出来,跳过就好
flag=false;
stsum=sum/i;//长木棍长度为i时,有stsum条长木棍
dfs(i,0,0,st[0]);
if(flag)//如果成立
break;
}
if(i>sum/2) printf("%d\n",sum);
else printf("%d\n",ans);
return 0;
}
其实吧,这道题,直接输出sum/n,就有30分
如果,过滤掉大于50的小木棍,再输出average值,就有80分了噢