题目大意:给出一些短棍子,问这些短棍子可不可以刚好凑出一些长度相等的长棍子。
很容易想到该题是一个搜索题,搜索时,从小到大不停枚举答案(需要凑的长棍子的长度),然后判断该长度是否可行,可行即是说用一根或者多根短棍能恰好凑出每一根长棍,所以搜索的过程就是凑长棍的过程。
该题主要的难点在于剪枝,主要的剪枝方法如下(有了这几个剪枝就可以0秒通过):1.先对所有的短棍子从大到小排序,每次在凑一根长棍时,都先用长的短棍去尝试,以后都用更短的短棍去尝试,严格来讲,这不应该说是一种剪枝,应该称为一种搜索时常用的方法,因为它并没有减少搜索树的大小,但是却可以使搜索更快的找出答案。
2.如果当前枚举的长棍的长度不能被所有短棍的长度之和整除,那么这个长度肯定是凑不出来的,跳过,这个最容易想到的剪枝了。
3.在凑一根长棍时,如果发现一根短棍和它前面的短棍的长度相同,可是前面的短棍没有用,那就说明这种长度的短棍是不能用在这个位置的,所以可以直接跳过。
4.在凑一根长棍时,如果发现某一根短棍是凑成一根长棍的第一根短棍,但是这个尝试却失败了,那么以后的尝试都不用再做了,直接返回false,因为这一根短棍按照题目的要求,它是必须要用于凑某根长棍的,既然它在现在作为第一根长棍都凑不出,那么以后不管它放在哪个位置,它都凑不出。
5.在凑一根长棍时,如果发现某一根短棍刚好是凑成一根长棍的最后一根短棍,但是以后的尝试却失败了,那这也同样意味着以后的尝试都不用再做了。原因如下:现在这一根短棍尝试失败了,那么只能找后面的多根长度之后恰好等于这根短棍的多根短棍来代替它,那么既然原来的多根短棍在后面都凑不出的东西,用这根稍长的短棍更凑不出,因为这根稍长的短棍能做的事,原来的多根更短的短棍都能做,相反则不一定。
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define N 65
int sticks[N], cur, num, n;
bool used[N];
int cmp(int a, int b){return a > b;}
bool dfs(int sides, int left, int start)
{
//凑成一根长棍了,继续凑下一根长棍,
if(left == 0){sides++, left=cur, start=0;}
//如果凑成了num条长棍,就成功了。
if(sides == num)
return true;
for(int i=start; i<n; i++)
{
if(sticks[i] > left || used[i]) continue;
if(i && !used[i-1] && sticks[i] == sticks[i-1]) continue;//剪枝3
used[i] = true;
if(dfs(sides, left-sticks[i], start+1))//方法1,以后都用更短的短棍去凑
return true;
used[i] = false;
if(left == cur || left == sticks[i]) return false;//剪枝4、5
}
return false;
}
int main()
{
int sum;
while(scanf("%d", &n), n)
{
sum = 0;
for(int i=0; i<n; i++)
{
scanf("%d", &sticks[i]);
sum += sticks[i];
}
sort(sticks, sticks+n, cmp);
for(cur = sticks[0];;cur++)
{
if(sum % cur != 0) continue;//剪枝2
num = sum / cur;
memset(used, false, sizeof(used));
if(dfs(0, cur, 0)) break;
}
printf("%d\n", cur);
}
return 0;
}