参考(其实是完全移植了)这里的解题报告: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; }