题目描述:
乔治有一些同样长的小木棍,他把这些木棍随意砍成几段,直到每段的长都不超过50 。现在,他想把小木棍拼接成原来的样子,但是却忘记了自己开始时有多少根木棍和它们的长度。给出每段小木棍的长度,编程帮他找出原始木棍的最小可能长度。
输入格式
第一行为一个单独的整数 N表示砍过以后的小木棍的总数。 第二行为 N个用空格隔开的正整数,表示 N 根小木棍的长度。
输出格式
输出仅一行,表示要求的原始木棍的最小可能长度。
样例
样例输入
9
5 2 1 5 2 1 5 2 1
样例输出
6
数据范围与提示
1<=N<=60
题意:同样长的几根木棍分成几段小木棍,求原始木棍的长度
思路:这几根木棍同样长,那么分成的小木棍长度的总和能被木棍长度整除,并且木棍的长度大于等于最大小木棍的长度,小于等于木棍总长度,所以在这之间从小到大寻找木棍长度len,然后与总长度相除,寻找到一共需要m根木棍,然后通过深搜判断小木棍能否满足组成m根长度为len的木棍,如果能组成,即为最小木棍长度。
#include <bits/stdc++.h>
using namespace std;
const int M = 1e5 + 10;
///a[]存储小木棍长度,len木棍长度,maxx总长度
///book[]标记,n小木棍数量,m需要多少根木棍
int a[M], len, maxx, book[M], n, m;
///布尔标记
bool flag;
///数组从大到小排序
bool cmp(int x, int y)
{
return x > y;
}
void dfs(int k, int use, int l)
/*
k表示第几根木棍
use表示使用的小木棍,然后从下一个小木棍开始找
l表示木棍长度减去使用的小木棍长度后的长度
*/
{
int i, j;
///木棍长度为0表示找完一根木棍
///开始找下一根木棍
if (l == 0)
{
///当找完这根木棍后木棍数等于所需木棍数时返回
if (k == m)
{
flag = true;
return;
}
///从大到小遍历小木棍的长度,寻找到未标记的小木棍
for (i = 1; i <= n; i++)
{
if (book[i] == 0)
break;
}
///使用未标记的长度最大的小木棍开始拼接下一个木棍
book[i] = 1;
///这里开始拼接第k+1个的木棍,长度从len开始减
dfs(k + 1, i, len - a[i]);
book[i] = 0;///回溯
}
///从使用过的小木棍的下一个小木棍开始找
for (i = use + 1; i <= n; i++)
{
///保证小木棍未使用过并且不能大于当前木棍所需长度
if (book[i] == 0 && a[i] <= l)
{
book[i] = 1;
dfs(k, i, l - a[i]);
book[i] = 0; ///回溯
///当len满足条件被标记时,直接返回
if (flag == true)
return;
///将重复的小木棍删去,剪枝
while (a[i] == a[i + 1])
i++;
}
}
}
int main()
{
while(~scanf("%d",&n)&&n)
{
int i, j, k, s = 0;
memset(book, 0, sizeof(book));
maxx = 0;
for (i = 1; i <= n; i++)
{
cin >> a[i];
maxx += a[i];
}
sort(a + 1, a + 1 + n, cmp);
///木棍的长度len大于等于最长的小木棍小于等于总长度
for (len = a[1]; len <= maxx; len++)///遍历
{
///木棍是相等的,所以木棍的长度能整除木棍总长度
if (maxx % len != 0)
continue;
flag = false;
///找到后看需要几根木棍
m = maxx / len;
///深搜看小木棍是否能够组成m根长度为len的木棍
///从第一个开始,标记第一个小木棍
book[1] = 1;
dfs(1, 1, len - a[1]);
///因为木棍长度是从小到大遍历的,所以找到的满足条件的第一个木棍长度即为最小长度
if (flag == true)
{
cout << len << endl;
break;
}
}
}
return 0;
}