首页 > 代码库 > search1011

search1011

题意:有一些 木棍,它们的长度相同。现在将木棍折断,最多折成64根。

编程求木棍可能的最小长度。

 

越长的木棍对后面木棍的约束力越大,因此要把小木棍排序,按木棍长度从大到小搜索,这样就能在尽可能靠近根的地方剪枝。(剪枝一)

          如果当前木棍能恰好填满一根原始木棍(可能已经是第二层甚至第NDFS了,此时原始木棍长度要小于最外层原始木棍长度),但因剩余的木棍无法组合出合法解而返回,那么后面的遍历方针是是用更短的木棍组合来代替这根木棍,他们的总长恰好是当前木棍的长度(直接用长的都无法最终成功,用了小的更不可能成功,因为小的、零碎的用途更多),但因为当前木棍的做的事和这些替代木棍之和做的事一样,直接退出当前的枚举。(剪枝二)

         显然最后一根木棍是不必搜索的

         考虑每根原始木棍的第一根木棍,如果当前枚举的木棍长度无法得出合法解,就不必考虑下一根木棍了,当前木棍一定是作为某根原始木棍的第一根木棍的,现在不行,以后也不可能得出合法解。也就是说每根原始木棍的第一根小木棍一定要成功,否则就返回。(剪枝四)

         剩下一个通用的剪枝就是跳过重复长度的木棍(剪枝五)

 

#include <stdio.h>#include <string.h>#include <algorithm>using namespace std;int used[100],len[100],sum,n,Min,s[100];int find(int p,int rest,int trest){    int i;    if (trest==Min) return 1;	//剩余所有的小木棍之和恰好是需要凑成的MIN    for (i=p;i<=n;i++)       if (!used[i]&&len[i]<=rest)//这里必须要遍历,考虑某个组合木棍还缺少4,如果剩余的仅仅有3 2 2,//如果把3加上则永远得不到成功的,因为必须要用2+2.       {          used[i]=1;          if (len[i]==rest)          {             if (find(1,Min,trest-len[i])) return 1;			 //本次组合成功,查看其余小木棍能否成功因此//从1开始return不仅仅跳出循环,还跳出整个函数          } else if (find(i+1,rest-len[i],trest-len[i])) return 1;//本次组合未完成,从更小的(i+1)开始搜索其余部分,使得和为rest          used[i]=0;//不成功如果成功不会进行到这里,因为前面已经RETURN跳出整个函数          if (len[i]==rest) return 0;//剪纸2,跳出循环,不用再遍历了,这里的遍历即是用别的组合来得到rest          if (rest==Min) return 0; //剪纸4,跳出循环,不用再遍历了          while (len[i+1]==len[i]) i++;//(剪枝五)       }    return 0;}bool cmp(int a,int b) {return a>b;}int main(){    int i;    while (scanf("%d",&n)&&n!=0)    {       for (i=1;i<=n;i++)           scanf("%d",&len[i]);       memset(used,0,sizeof(used));       sort(len+1,len+n+1,cmp);       sum=0;       for (i=1;i<=n;i++) sum+=len[i];       Min=len[1];       while (sum%Min!=0) Min++;       s[n+1]=0;       while (find(1,Min,sum)==0)       {          Min++;          while (sum%Min!=0) Min++;       }       printf("%d\n",Min);    }    return 0;

  

search1011