据说这题是数据加强版?对不起,我没看出来。
题目描述 乔治有一些同样长的小木棍,他把这些木棍随意砍成几段,直到每段的长都不超过5050。 现在,他想把小木棍拼接成原来的样子,但是却忘记了自己开始时有多少根木棍和它们的长度。 给出每段小木棍的长度,编程帮他找出原始木棍的最小可能长度。 输入输出格式 输入格式: 共二行。 第一行为一个单独的整数N表示砍过以后的小木棍的总数,其中N≤65N≤65 (管理员注:要把超过5050的长度自觉过滤掉,坑了很多人了!) 第二行为NN个用空个隔开的正整数,表示NN根小木棍的长度。 输出格式: 一个数,表示要求的原始木棍的最小可能长度
Input
9 5 2 1 5 2 1 5 2 1
Output
6
这题做法太显然了啊。狂野bfs爆搜就可以一波带走...........的说。当然您要是直接枚举的话,出门右转TLE欢迎您。(别问我为什么右转)
先来一段普通剪枝:
- dfs判断情况时从最长的木棍枚举到最短的木棍,显然能与最长的木棍组成当前需要的长度的木棍的个数是要比长度短的少的。而且长度不超过50,一个桶排搞定,常数小且方便判断和回溯。
- 木棍个数应该为整数,所以枚举的长度应该能整除总长度。
- 当搜索时已经使用了长度为x的木棍,那么下次就可以直接从长度为x开始枚举,显然比x长的都不行了,不然当前就不会使用x了。
- 对于某次拼接时,当前拼好的长度为0或当前长度加上先前枚举的长度等于需要长度,直接跳出循环。这是因为我们是递减枚举,显然再往后搜是不能再从那些长度小的木棍中拼出当前的长度了。
显然这样挑一些就是可以过的啊(比如 我1618ms 1040KB就过了,虽然超慢的说QAQ)当然您全剪了也不会有事的啊。
一定要注意木棍长度不大于50,所以输入的木棍长度中要忽略长度超过50的木棍(是我被坑的地方啊,绝对有人没看见!!)
好了放代码(丑陋至极,不想看别看了,自己写也很容易啊)-->毕竟是很久以前的代码了 丑。
#include<iostream> #include<cmath> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; int n,m,res,maxn,sum,cnt; int gg[70]; bool flag,vis[70]; int cmp(int a,int b) { return a>b; } int dfs(int l,int sta,int now) { if(now==res) return 1; if(l==0) if(dfs(m,1,now+1)) return 1; for(int i=sta;i<=cnt;i++) { if(!vis[i]&&gg[i]<=l) { vis[i]=1; if(dfs(l-gg[i],i+1,now)) return 1; vis[i]=0; if(l==m||l==gg[i]) break; while(gg[i]==gg[i+1]) i++; } } return 0; } int main() { scanf("%d",&n); int x; for(int i=1;i<=n;i++) { scanf("%d",&x); if(x>50) continue; gg[++cnt]=x; sum+=x;//总长 } sort(gg+1,gg+cnt+1,cmp); for(int i=gg[1];i<=sum;i++) { if(sum%i==0) { m=i; res=sum/i; if(dfs(i,1,0)) { cout<<i<<endl; return 0; } } } return 0; }
那这道题就轻松水过了。
谢谢。
1、 木棍数目枚举范围:最大为 长度和/最大长度
2、 木棍数目是否符合要求:木棍数目为总长度的约数。
3、 后缀和判断解不可行。
4、 Nowi的使用减少同一根小棒的枚举
5、 每次组装新的木棍的时候人为选择最长的木棍(加速枚举中nowlen+a[i]<len的判断,否则会多拓展一层)