POJ 1011 Sticks

题目大意是:

—乔治拿来一组等长的木棒,将它们随机地砍断,使得每一节木棍的长度都不超过50个长度单位。然后他又想把这些木棍恢复到为裁截前的状态,但忘记了初始时有多少木棒以及木棒的初始长度。请你设计一个程序,帮助乔治计算木棒的可能最小长度。每一节木棍的长度都用大于零的整数表示。
看到这道题首先想到的就是暴力法枚举。枚举原始长度dfs判断该长度是否可以由小木棒组合而成。不过直接这样肯定超时,自然想到要添加剪枝;
—剪枝策略:
—1、已知木棒总长度sum,原始木棒长度L一定满足sum%L = 0;
—2、将木棒从大到小排序,因为越长的木棒对后面的约束越大;
—3、当一根木棒搜索失败时,跳过与它相同长度的;
—4、如果每次开始搜索的第一根木棍找不到可以组成原长的组合,则后面没有必要再搜索下去了;
—5、。。。目前想到这么多。。。
接下来看代码吧:
#include <iostream>
#include <algorithm>
#define MAX 1000
using namespace std;
int a[MAX], used[MAX];//用于记录下标为i的木棒是否被用
int cmp(const void *a, const void *b)
{
return *(int *)b - *(int *)a;
}
bool dfs(int total, int unused, int left, int len)//分别代表总共的木棒数,未用过的木棒数,当前拼接的木棒还差的长度,所枚举的当前木棒长度
{
int i;
if(unused == 0 && left == 0)//如果木棒都用完,并且最后一根木棒也拼接完毕,表示当前所枚举的这个木棒长度可行,是最优解
return true;
if(left == 0)
left = len;//拼接下一根
for(i = 0; i < total; i++){
if(used[i] == 1)//这根已用过
continue;
if(a[i] > left)//拼不成,枚举下一根
continue;
used[i] = 1;
if(dfs(total, unused - 1, left - a[i], len))//从这里递归搜索
return true;
used[i] = 0;//返回时要记得把used花园
if(a[i] == left || left == len)//这里是最后一层剪枝
break;
}
return false;
}
int main()
{
int n;
while(scanf("%d", &n), n){
int i, max;
int sum = 0;
memset(used, 0, sizeof(used));
for(i = 0; i < n; i++){
scanf("%d", &a[i]);
sum += a[i];
}
qsort(a, n, sizeof(int), cmp);//按降序排列,也算是重要的一个剪枝
max = a[0];//从最长的一根开始枚举
for(i = max; i <= sum; i++){
if(sum%i != 0)//这一步是非常重要的剪枝,因为初始的木棒长度必然要能整除总长度
continue;
if(dfs(n, n, 0, i)){
printf("%d\n", i);
break;
}
return 0;
}
}
}
posted @ 2012-02-23 23:20  背着超人飞  阅读(205)  评论(0)    收藏  举报