[洛谷P1120] 小木棍 [数据加强版]
# 洛谷题目链接:[小木棍[数据加强版]](https://www.luogu.org/problemnew/show/P1120)
题目描述
乔治有一些同样长的小木棍,他把这些木棍随意砍成几段,直到每段的长都不超过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(请放心食用)
简述一下题意:给出n根木棍长度(长度大于50的自己过滤掉),判断可以由几根长度相同的木棍截成这些长度. 输出最小的长度.
样例由(1+5),(1+5),(1+5),(2+2+2)组成
简单分析一下:
题目要求由几根长度相同的木棍能拆成这么多木棍,然而这个并没有任何规律能够直接推出这个长度,但是显然这个长度是没有什么公式能直接推出来的.所以考虑爆搜.
那么就考虑一下如何优化剪枝了.我们可以得到一下优化策略:
- 题目要求要由整数根的木条拆成这些小木棍,那么可以在枚举原木棍长度的时候先判断,能被所有木棍长度之和整除的才可能是答案.
- 原木棍长度至少是所有木棍中长度最大的那根,否则长度最大的那根小木棍无法被组成.
- 在搜索的时候可以从大到小搜,因为长度大的木棍能更快组成一根长木棍,这样可以减少分支数.
- 枚举到当前长度如果可以和已经组成的长度组成一根完整的木棍(或是目前组成的长度为0),那么在回溯的时候可以直接break.因为如果枚举到这个长度可以与之前组成的一些长度组成一根新的木棍,那么如果它能搜出最终的答案,就不会回溯了.如果不能搜出答案,那么继续往后枚举加入的长度也不会再搜出答案了.
- 由于数据小,所以可以直接桶排记录数据,枚举剩余木棍长度时只要枚举到所有木棍中的最小值.
- 枚举搜索的原木棍长度时只需要枚举到总长度的\(\frac{1}{2}\),如果搜到了总长度的\(\frac{1}{2}\)还没搜出答案的话,说明这些木棍一定是从一根木棍上切下来的.
然后稍微注意一下输入,没什么细节了.下面上代码:
#include<bits/stdc++.h>
using namespace std;
const int N=65+5;
const int inf=2147483647;
int n, cnt = 0, tot = 0, b[N];
int ans = inf, mx = -inf, mn = inf;
bool used[N];
bool cmp(int a,int b){
return a > b;
}
void dfs(int sti,int lim,int sum,int maxx){
if(sti == 0){printf("%d\n",lim); exit(0);}
if(sum == lim){dfs(sti-1,lim,0,mx); return;}
for(int i=maxx;i>=mn;i--){
if(i+sum <= lim && b[i]){
b[i]--;
dfs(sti,lim,sum+i,i);
b[i]++;
if(sum == 0 || sum+i == lim) break;
}
}
}
int main(){
int x; cin >> n;
for(int i=1;i<=n;i++){
cin >> x;
if(x <= 50){
b[x]++ , tot += x;
mx = max(mx , x);
mn = min(mn , x);
}
}
for(int i=mx;i<=tot/2;i++)
if(tot % i == 0) dfs(tot/i,i,0,mx);
printf("%d\n",tot);
return 0;
}