回溯经典剪枝——小木棍

来源:互联网 发布:python base64.decode 编辑:程序博客网 时间:2024/06/05 16:16

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

【输入格式】

  第一行是一个整数N,表示砍断之后共有多少节木棍。
  第二行是截断以后,所得到的各节木棍的长度。在最后一组数据之后,是一个零。

【输出格式】

  输出原始木棒的可能最小长度。

【输入样例】

9
5 2 1 5 2 1 5 2 1

【输出样例】

6

【数据范围】

N<=64
每节裁断后的小木棍长度不超过50。


很明显,如果纯暴力枚举拼成的大木棍len(tot%len==0)后,枚举分成的k堆包含哪些棍子(状态函数:bool run(int i,int rest,int p)表示第i堆还差rest的长度到len,第i堆中刚才选了第p根)需要O(x*n!)的时间。。。好像慢得不止一点。
所以优化剪枝很必要。
大概的思路有:
1.减少搜索步骤(如n个状态搜索到n-1层并计算第i层);
2.加强每一步的约束条件;
3.预测性剪枝(最重要的剪枝)

本题大概有四种剪枝思路:
1.将小木棍从大到小排序:我们发现如果小段的木棍先进“队”的话回溯的层次将会比大段的木棍先进多得多。(大段的木棍约束性会更强些)。
*2.如果每堆的第一根木棍判断出无解则返回false:因为木棍在不同的堆中都可以作为第一根,所以如果木棍在这一堆中不满足,则到其他堆去同样不满足。
*3.如果放入某堆中的最后一根木棍刚好rest==0但后续不满足则返回false:原因同上。
*4.之后选择的元素!=之前选择的元素:选后效果将会是一样的,返回false还会是返回false。实现时需注意可能当前的元素可能会因为和上一个vis=1(用过)的数相等而被continue,所以这里用一个失败参数记录上一次失败用的元素。


#include<cstdio>#include<iostream>#include<algorithm>#include<cmath>#include<cstring>#include<cctype>#include<vector>#include<map>using namespace std;const int maxn=80;int n,a[maxn],maxv,tot,vis[maxn],k,len;bool cmp(int a,int b){    return a>b;}bool run(int i,int rest,int p)//第i堆还差rest的长度到len,第i堆中刚才选了第p根 {    if(i>k)//共k堆     {        return 1;    }    int fail=0;     for(int x=p+1;x<=n;x++)//从第p+1根开始选    if(!vis[x])    {         if(a[x]==a[fail]) continue;//注意continue和return        if(rest>a[x])         {            vis[x]=1;            bool w=run(i,rest-a[x],x);            vis[x]=0;            if(!w) fail=x;            if(w) return 1;        }        else if(rest==a[x])        {            vis[x]=1;            bool w=run(i+1,len,0);            vis[x]=0;            return w;        }        if(p==0) return 0;     }    return 0;}int main(){    scanf("%d",&n);    for(int i=1;i<=n;i++)     {        scanf("%d",&a[i]);        maxv=max(maxv,a[i]);        tot+=a[i];    }    sort(a+1,a+1+n,cmp);    for(len=maxv;len<=tot;len++)    if(tot%len==0)    {        k=tot/len;        if(run(1,len,0))         {            printf("%d",len);            break;        }    }    return 0;}
原创粉丝点击