题目描述:
乔治有一些同样长的小木棍,他把这些木棍随意砍成几段,直到每段的长都不超过50。现在,他想把小木棍拼接成原来的样子,但是却忘记了自己开始时有多少根木棍和它们的长度。给出每段小木棍的长度,编程帮他找出原始木棍的最小可能长度。
输入输出格式输入格式:
输入文件共有二行。第一行为一个单独的整数N表示砍过以后的小木棍的总数,其中N≤65(管理员注:要把超过50的长度自觉过滤掉,坑了很多人了!)第二行为N个用空个隔开的正整数,表示N根小木棍的长度。输出格式:输出文件仅一行,表示要求的原始木棍的最小可能长度
输入输出样例
输入样例#1:95 2 1 5 2 1 5 2 1
输出样例#1:6
---------------------------
各种剪枝
基本思路:
从小到大遍历原来木棍可能的长度
把木棍排序,按从大到小的顺序填入原来的木棍中.
具体剪枝直接看代码
#include<stdio.h>
#include<algorithm>
#include<string.h>
using namespace std;
const int maxN = 65;
int n;
int a[maxN];
int vis[maxN];
int DFS(int lim, int len, int cnt, int last)
{
if(len == lim)
len = 0, last = n - 1;
if(cnt == n)
return 1;
int flag = 0;
for(int i = last; i >= 0; i --) //last用于剪枝
if(! vis[i] && a[i] + len <= lim)
{
if(i < n - 1 && a[i] == a[i + 1] && ! vis[i + 1]) //剪枝
continue;
vis[i] = 1;
if(DFS(lim, len + a[i], cnt + 1, i))
return 1;
vis[i] = 0;
if(len == 0 || a[i] == lim - len)
//剪枝.对于一个原长度,假如它是可行的
//则当其为空的时候,第一位无论填入什么,都应该是可以成立的.假如第一位填入的就不成立,可直接退出
//假如当前长度等于剩余长度且不可行,则其后的填法肯定都不可行
return 0;
}
return 0;
}
int main()
{
scanf("%d", &n);
int sum = 0;
for(int i = 0; i < n; i ++)
{
scanf("%d", &a[i]);
if(a[i] > 50)
i --, n --;
else
sum += a[i];
}
sort(a, a + n);
for(int i = a[1]; i <= sum / 2; i ++)
if(sum % i == 0) //剪枝
{
memset(vis, 0, sizeof(vis));
if(DFS(i, 0, 0, n - 1))
{
printf("%d", i);
return 0;
}
}
printf("%d", sum);
}