参考(其实是完全移植了)这里的解题报告:http://blog.sina.com.cn/s/blog_6635898a0100lgq0.html。
感觉搜索题做伤了。短期之内不会再做了。
首先,sticks要从大到小排序。因为大的stick相对难找,一开始就确定好有利于更快地确定解。
其次,记下当前正在匹配的stick,下次从这个stick之后开始试。
再次,如果当前的stick和前一个stick的长度一样,而前一个stick没有选(前一个stick肯定一个尝试过了,没选是因为没有解),说明这个stick也不用尝试了。
个人认为要点在于链接中的第3点,即如果当前len等于0,即(匹配完了上一段或最开始),新开始一段时,只需要取第一个没选的(也是最大的,因为sticks已经从大到小排序好了。)。不用再像普通循环里面的那样,一个一个尝试了。因为如果有解,这个第一个stick迟早要选,而且位置在哪里都不关心,所以第一个选它就可以了。
code已经改得和链接上面几乎完全一样了。这里为了日后记录,遂粘贴与此。
#include <algorithm>
#include <iostream>
using namespace std;
const int MAXN = 65;
int N, len;
int sticks[MAXN];
bool mark[MAXN];
bool flag;
void recur(int depth, int l, int k)
{
if(flag)
{
return;
}
if(l == 0)
{
int i = 0;
while(mark[i])
{
i++;
}
mark[i] = true;
recur(depth + 1, sticks[i], i + 1);
mark[i] = false;
return;
}
if(l == len)
{
if(depth == N)
{
flag = true;
}
else
{
recur(depth, 0, 0);
}
return;
}
for(int i = k; i < N; i++)
{
if(!mark[i] && l + sticks[i] <= len)
{
if(!mark[i - 1] && sticks[i - 1] == sticks[i])
{
continue;
}
mark[i] = true;
recur(depth + 1, l + sticks[i], i + 1);
mark[i] = false;
}
}
}
int main()
{
while(scanf("%d", &N) && N != 0)
{
flag = false;
int sum = 0;
for(int i = 0; i < N; ++i)
{
fscanf(stdin, "%d", &sticks[i]);
sum += sticks[i];
// fprintf(stdout, "sticks[%d]: %d\n", i, sticks[i]);
}
sort(sticks, sticks + N, greater<int>());
for(len = sticks[0]; len <= sum; ++len)
{
if(sum % len == 0)
{
// memset(mark, false, N * sizeof(bool));
memset(mark, false, sizeof(mark));
recur(0, 0, 0);
if(flag)
{
fprintf(stdout, "%d\n", len);
break;
}
}
}
}
return 0;
}