POJ 1011
拿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; }