您的位置:首页 > 其它

poj_1011_sticks(搜索+剪枝)

2013-05-23 14:05 288 查看
题型:搜索题

题意:此题堪称最经典搜索题。

Description

乔治拿来一组等长的木棒,将它们随机地裁断,使得每一节木棍的长度都不超过50个长度单位。然后他又想把这些木棍恢复到为裁截前的状态,但忘记了初始时有多少木棒以及木棒的初始长度。请你设计一个程序,帮助乔治计算木棒的可能最小长度。每一节木棍的长度都用大于零的整数表示。

Input

输入包含多组数据,每组数据包括两行。第一行是一个不超过64的整数,表示砍断之后共有多少节木棍。第二行是截断以后,所得到的各节木棍的长度。在最后一组数据之后,是一个零。

Output

为每组数据,分别输出原始木棒的可能最小长度,每组数据占一行。

Sample Input

9

5 2 1 5 2 1 5 2 1

4

1 2 3 4

0

Sample Output

6

5
分析:
一堆长度不等的木棍儿,如何分组使他们每一组的长度之和相等咧?穷举是肯定的,但是暴力是不可能的。。。

设这一堆木棍的总长度为sum,那么可以想到,想要将其平均分配,每组的总长度肯定是sum的约数,因此只需枚举sum的所有约数就好了;

用DFS解决是靠谱的,但是单纯的来写的话就会光荣的得到一个TLE,因为计算过程中很大一部分是不必要的,因此本题的技巧就在于如何剪枝;

我们要找原来木头可能的最小长度,那么可以用一个每组木棍长度之和的循环,循环的初始值是木棍中最大的长度,循环的边界是所有木棍长度之和,如此便可保证得到可能最小长度的木棍儿。

把木棍儿长度按从大到小进行排序。既可以减少运行时间,又方便剪枝。

好了,关键来了,我们怎么剪枝呢?

1、若要决策的木棍儿正好是需要的全部长度(即为当前拼接的最后一个)后续循环是想要较短的去拼接起来,显然即使拼了起来了,剩这个stick[i]迟早还是要用在某个地方的,所以不可行。

2、如果是重新开始找的,找到一个较短的棍子是不可行的,那么直接放弃这种方案,因为以某个短棍子不行的话,这个短棍子迟早还是要用到,那么,当然还是不可行的。

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include <algorithm>
using namespace std;
bool used[1000];
int a[1000];
int n,sum;

int cmp(const void *a,const void *b){
return (*(int *)b-*(int *)a);
}

bool dfs(int unused,int left,int len,int pos){//unused是木有用过的木棒的个数,left是生下来要拼的的长度,len是目标长度,pos用于记录当前位置
if(left==0&&unused==0){
return true;
}
if(left==0){
left=len;
pos=0;
}
for(int i=pos;i<n;i++){//剪枝1:记录当前位置,不用再重复遍历;
if(used[i]==true){//已用过的就不用了
continue;
}
if(a[i]>left){//若当前木棍长度大于剩余要拼接的长度,跳过;
continue;
}
used[i]=true;//标记
if(dfs(unused-1,left-a[i],len,i+1)){//深搜
return true;
}
used[i]=false;//状态恢复
if(len==left||a[i]==left){//剪枝2
break;
}
}
return false;
}

int main(){
while(scanf("%d",&n)&&n){
memset(used,false,sizeof(used));
sum=0;
for(int i=0;i<n;i++){
scanf("%d",&a[i]);
sum+=a[i];
}
qsort(a,n,sizeof(a[0]),cmp);
for(int i=n;i>0;i--){
if(sum%i==0&&sum/i>=a[0]){
if(dfs(n,0,sum/i,0)){
printf("%d\n",sum/i);
}
}
}
}
return 0;
}



                                            
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: