【POJ1011】Sticks-DFS+调整法剪枝

来源:互联网 发布:cs1.5优化教程视频 编辑:程序博客网 时间:2024/04/30 15:52

测试地址:Sticks

题目大意:有n根小棍,要拼成若干根长度相等的大棍,问大棍的最小长度是多少。

做法:很容易确定搜索思路:枚举大棍的长度,然后进行DFS查看是否可行,用一个bool函数dfs(r,l)表示当前剩余r根小棍未使用,当前大棍剩余长度为l的情况下是否有解。但是单纯这样裸搜,不知道要搜多少年呢...于是在看了一些资料之后,找到了一些剪枝思路:

1.如果当前大棍长度不是小棍长度之和的因数,不用考虑(应该是很容易就能想到的剪枝)。

2.大棍长度不会小于最长小棍的长度,并且如果在大棍长度≤小棍长度之和/2的范围中如果无解,则结果就已经可以确定为小棍的长度之和(即将所有小棍拼成一根大棍),所以枚举范围为:最长小棍长度≤大棍长度≤小棍长度之和/2。

3.如果将小棍按长度由大到小排序,搜索的时间会显著减少。

4.在开始拼一根新的大棍时,要选还未被使用的最长的小棍作为最开始的小棍,如果发现以这根小棍开始不能拼成这样的大棍,则直接退回去调整上一根大棍。

5.用used数组来表示小棍使用过与否,如果在从小棍标号从小到大枚举的过程中,发现现在的小棍长度和上一根小棍长度相等,且上一根小棍没被使用,则判定为无解,因为如果有解则一定已经在搜索前一根小棍时就出解了。

6.为预防重复搜索,应记录最近拼接的小棍编号no,只用枚举编号比no大的即可(如果是开始拼接一根新的大棍,参照剪枝4)。

7.如果剩余长度等于当前所枚举的小棍长度,则说明该小棍正好填满这根大棍,但如果继续搜索后返回的结果是无解,则退回枚举上一根小棍。

以下是本人代码:

#include <cstdio>#include <cstdlib>#include <cstring>#include <iostream>#include <algorithm>using namespace std;int n,len[65],tot,no,i;bool used[65];bool cmp(int a,int b){  return a>b;}bool dfs(int r,int l){  if (r==0&&l==0) return 1;  if (l==0) l=i;  int start=1;  if (l!=i) start=no+1; //剪枝6  for(int j=start;j<=n;j++)    if (!used[j]&&len[j]<=l){  if (j>1&&!used[j-1]&&len[j-1]==len[j]) continue; //剪枝5  used[j]=1;no=j;  if (dfs(r-1,l-len[j])) return 1;  used[j]=0;  if (len[j]==l||l==i) return 0; //剪枝4,7}  return 0;}int main(){  while(scanf("%d",&n)&&n)  {    tot=0;for(i=1;i<=n;i++){  scanf("%d",&len[i]);  tot+=len[i];}sort(len+1,len+n+1,cmp); //剪枝3for(i=len[1];i<=tot/2;i++) //剪枝2  if (!(tot%i)) //剪枝1  {    memset(used,0,sizeof(used));no=1;if (dfs(n,i)){  printf("%d\n",i);  break;}  }if (i>tot/2) printf("%d\n",tot);  }    return 0;}


0 0