题目描述
乔治有一些同样长的小木棍,他把这些木棍随意砍成几段,直到每段的长都不超过50。
现在,他想把小木棍拼接成原来的样子,但是却忘记了自己开始时有多少根木棍和它们的长度。
给出每段小木棍的长度,编程帮他找出原始木棍的最小可能长度。
输入输出格式
输入格式:
输入文件共有二行。
第一行为一个单独的整数N表示砍过以后的小木棍的总数,其中N≤65
(管理员注:要把超过50的长度自觉过滤掉,坑了很多人了!)
第二行为N个用空个隔开的正整数,表示N根小木棍的长度。
输出格式:
输出文件仅一行,表示要求的原始木棍的最小可能长度
输入输出样例
说明
2017/08/05
数据时限修改:
-#17 #20 #22 #27 四组数据时限500ms
-#21 #24 #28 #29 #30五组数据时限1000ms
其他时限改为200ms(请放心食用)
搜索题,剪枝很重要
#include <bits/stdc++.h> using namespace std; #define maxn 100000 typedef long long ll; #define inf 2147483647 #define ri register int int n; int a[maxn]; int cnt = 0;//真正的总个数 int tot = 0;//真正的总和 bool used[maxn]; int x; bool cmp(int x, int y) { return x > y; } //G组数,sum当前已装载大小,target每组目标大小,cur当前遍历到的位置 void dfs(int G, int sum, int target, int cur) { if (G == 0) { cout << target; exit(0); } if (sum == target) { dfs(G - 1, 0, target, 1); return; } if (sum + a[cnt] > target) //最小的都放不进直接pass return; for (int i = cur; i <= cnt; i++) { if (used[i]) continue; //平淡的回溯 used[i] = true; sum += a[i]; dfs(G, sum, target, i); sum -= a[i]; used[i] = false; if (sum == 0 || sum + a[i] == target) //如果像这样已经满足目标的都exit不了,那么就没必要继续下去,因为接下来装的数目的也是跟之前一样的 break; while (a[i] == a[i + 1]) //这个数都过不了,相同的数也别想过 i++; } } int main() { ios::sync_with_stdio(false); // freopen("test.txt", "r", stdin); // freopen("outout.txt","w",stdout); cin >> n; for (int i = 1; i <= n; i++) { cin >> x; if (x <= 50) { a[++cnt] = x; tot += x; } } sort(a + 1, a + 1 + cnt, cmp); //从大到小排次序,能优化不少 for (int i = a[1]; i <= tot / 2; i++) { if (tot % i == 0) dfs(tot / i, 0, i, 1); } cout << tot; return 0; }