题目描述
乔治有一些同样长的小木棍,他把这些木棍随意砍成几段,直到每段的长都不超过50。
现在,他想把小木棍拼接成原来的样子,但是却忘记了自己开始时有多少根木棍和它们的长度。
给出每段小木棍的长度,编程帮他找出原始木棍的最小可能长度。
输入输出格式
输入格式:
输入文件共有二行。
第一行为一个单独的整数N表示砍过以后的小木棍的总数,其中N≤65
(管理员注:要把超过50的长度自觉过滤掉,坑了很多人了!)
第二行为N个用空个隔开的正整数,表示N根小木棍的长度。
输出格式:
输出文件仅一行,表示要求的原始木棍的最小可能长度
输入输出样例
输入样例#1:
9
5 2 1 5 2 1 5 2 1
输出样例#1:
6
说明
2017/08/05
数据时限修改:
-#17 #20 #22 #27 四组数据时限500ms
-#21 #24 #28 #29 #30五组数据时限1000ms
其他时限改为200ms(请放心食用)
分析:CL大佬讲课的时候讲到了这题,于是就来做一下。但是讲的时候没听,于是做的时候遇到了瓶颈,还是参考了five20大佬的博客。确定了搜索的思路,那么要考虑的就是如何剪枝,这题真的是一道练习搜索剪枝的绝(e)世(xin)好(gui)题。总共需要用到一下几种剪枝:
1,搜索范围:很显然,答案肯定是在[maxn,sum]内,maxn为给定木棍的最大长度,sum为给定木棍长度之和,同时正确答案一定能被sum整除,所以搜的时候范围就确定在maxn到sum/2之间能把sum整除的所有整数。如果在该范围内均不满足,那么答案就是sum。
2,搜索顺序:由上面得出的范围可知,从大到小搜索更优,而且根据题目可知,木棍长度<=50,那么也不需要快排了,直接桶排,记录每种长度的木棍的数目即可。
3,如果搜索过程中已经使用过长度为x的木棍,那么下一层搜索直接从x开始从大到小搜,因为显然此刻使用了x那么比x长的都已经不可行了。
4,如果某次搜索时如果当前长度为0或当前长度加选择木棍的长度等于目标长度,那么搜完以后直接break,因为是从大到小枚举,因此再小的木棍就不能达到目标长度了。(这个优化可能这么说并不太好理解,结合代码应该就好懂了)
Code:
//It is made by HolseLee on 19th Apr 2018 //Luogu.org P1120 #include<bits/stdc++.h> using namespace std; const int N=101; int n,num[N],sum,cnt; int maxn=0,minn=N; inline void dfs(int tar,int now,int len,int m) { if(!tar){printf("%d",len);exit(0);} if(now==len){ dfs(tar-1,0,len,maxn); return;} for(int i=m;i>=minn;i--){ if(now+i<=len&&num[i]){ num[i]--; dfs(tar,now+i,len,i); num[i]++; if(!now||now+i==len) break;} } } int main() { ios::sync_with_stdio(false); cin>>n;int x; for(int i=1;i<=n;i++){ cin>>x; if(x<=50){ num[x]++;cnt++; maxn=max(maxn,x); minn=min(minn,x); sum+=x;} } for(int i=maxn;i<=sum/2;i++){ if(sum%i==0)dfs(sum/i,0,i,maxn); } printf("%d",sum); return 0; }