拿2362的代码来交果断超时,研究了半天也没整出来。看了大神的剪枝方法,简直五体投地。详见代码。
参考博客:http://blog.sina.com.cn/s/blog_7d3ee9f50100wiy4.html
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
bool cmp(int a, int b) {
return a > b;
}
int n, len;
int s[70], book[70];
int dfs(int left, int num, int start) { //left表示组成这跟原木棍还需要多少长度
//num表示还有几根原木棍没拼成
//start表示从哪根现木棍开始扫
if(left == 0) {
if(num == 0) return 1; //搜索退出条件
else {
int i;
for(i = 0; book[i]; i++); //剪枝,找到第一个没被使用的现木棍
book[i] = 1; //由于从大到小排序,所以这第一根现木棍一定会被用到,标记
//因为假设这次用不到,以后每一根木棍总会被用到
if(dfs(len - s[i], num - 1, i + 1)) return 1;
//从第一根没用到过的木棍的下一个开始搜索
book[i] = 0;
}
}
else {
int i;
for(i = start; i < n; i++) {
if(i > 0 && s[i] == s[i -1] && book[i - 1] == 0) continue;
//剪枝,若此木棍和前一个木棍长度相等,
//而前一个木棍没有被用到,那么这根木棍一定也用不到
if(!book[i] && left >= s[i]) { //简单剪枝,此木棍一定不能大于left
book[i] = 1;
if(dfs(left - s[i], num, i + 1)) return 1; //忘下一层搜
book[i] = 0;
if(s[i] == left) break; //一个强大的剪枝, 如果此木棍恰好可以组成这一条原木棍,
//但是往下搜索却失败了(不失败的话就会在上一个if语句return,执行不到这一步了)
//而如果继续往下搜的话,必定要找到其他多条现木棍来代替此木棍填满这根原木棍,
//但无论找什么代替,代替后的搜索情况一定包含在上一次失败的搜索中了
//(因为此木棍将用来填补以后的空缺,而这些空缺用可代替的那些木棍来填补也是一样,
//而这种情况在上次失败的搜索中已经试过了) ,没必要重复,因此退出循环返回上一层
}
}
}
return 0;
}
int main() {
//freopen("input.txt", "r", stdin);
while(~scanf("%d", &n) && n) {
int i, sum = 0;
for(i = 0; i < n; i++) {
scanf("%d", s + i);
sum += s[i];
book[i] = 0;
}
sort(s, s + n, cmp); //降序排列可以提高效率
int num;
for(i = n; i >= 1; i--) { //i表示原木棍的总数,一定小于n,
//又因为要找最短长度,所以木棍数量应从大到小数
if(sum % i == 0 && sum / i >= s[0]) { //剪枝1,每根原木棍长度一定是整数
//并且一定大于等于所有现木棍
num = i; //搜索的初始化
memset(book, 0, sizeof(book));
len = sum / i;
if(dfs(len, num - 1, 0)) { //只需要搜n-1根木棍即可
printf("%d\n", len);
break; //第一个满足条件的便是最小的,此时退出
}
}
}
}
return 0;
}