Sticks(经典深搜+剪枝)

#include <iostream>
#include <algorithm>
using namespace std;

//total能组成的木棒的组数,l:组成的木棒的长度
int total,l;
//num:输入的整数,sum:总长度
int num,sum;
typedef struct
{
    int length;//木棒的长度
    bool mark;//木棒是否被使用过
}Sticks;
Sticks sticks[70];

bool cmp(Sticks a,Sticks b)
{
    return a.length>b.length;
}
//s 已组成的木棒数目,len已经组成的长度,pos搜索的木棒的下标的位置
int dfs(int s,int len,int pos)
{
    if(s==total)return 1;
    for(int i=pos+1;i<num;i++)
    {
        //如果这个木棒已经用过,则继续下一根
        if(sticks[i].mark)continue;
        if(len+sticks[i].length == l)
        {
            sticks[i].mark = true;
            if(dfs(s+1,0,-1))return true;
            sticks[i].mark = false;
            return false;
        }else if(sticks[i].length+len<l){
            sticks[i].mark = true;
            if(dfs(s,len+sticks[i].length,i))
            return true;
            sticks[i].mark = false;
            //剪枝:如果当前搜索时,前边的长度为0,而第一根没有成功的使用,
            //说明第一根始终要被废弃,所以这种组合必定不会成功
            //此处的剪枝必须有,因为这里的剪枝会节省很多的无用搜索,
            //我试了一下,此处剪枝省去就会超时的。。。。
            if(len==0)return false;
            //剪枝:如果当前和上一次搜到的木棒是一样长的则没必要再搜一次了
            while(sticks[i].length==sticks[i+1].length)i++;
        }
    }
    return false;
}

int main()
{

    while(cin>>num&&num!=0)
    {
        sum = 0;//标记为0
        for(int i = 0; i < num; i++)
        {
            cin>>sticks[i].length;
            sum += sticks[i].length;
            sticks[i].mark = false;
        }
        //将木棒按照长度从长到短的顺序排序
        sort(sticks,sticks+num,cmp);
        //从木棒的最长的那根开始搜索,因为最小的组合也会大于等于最长的那根
        for(l = sticks[0].length; l <= sum; l++)
        {
            //剪枝一:如果不能被整除说明不能组成整数根木棒,搜下一个
            if(sum%l!=0)continue;
            total = sum / l;//得到木棒总数目
            if(dfs(1,0,-1))
            {
                cout<<l<<endl;
                break;
            }
        }
    }
    return 0;
}
经典搜索题,果然是到处充斥着剪枝才能过啊,我的代码离剪到极限还差很多
题目给出一大堆小棍子的长度,需要把他们拼成几根长度相等的大棍子,求大棍子的最短长度
 
 
#include <iostream>
#include <algorithm>
using namespace std;
int sticks[65];
int used[65];
int n,len;
bool dfs(int i,int l,int t)//i为当前试取的棍子序号,l为要拼成一根完整的棍子还需要的长度,t初值为所有棍子总长度 
{
    if(l==0)
    {
        t-=len;
        
        if(t==0)return true;
        
        for(i=0;used[i];++i);            //剪枝1:搜索下一根大棍子的时候,找到第一个还没有使用的小棍子开始
        
        used[i]=1;                           //由于排序过,找到的第一根肯定最长,也肯定要使用,所以从下一根开始搜索
        if(dfs(i+1,len-sticks[i],t))return true;
        used[i]=0;
            
        t+=len;
    }
    else
    {
        for(int j=i;j<n;++j)
        {
            if(j>0&&(sticks[j]==sticks[j-1]&&!used[j-1]))  //剪枝2:前后两根长度相等时,如果前面那根没被使用,也就是由前面那根
                continue;                                                //开始搜索不到正确结果,那么再从这根开始也肯定搜索不出正确结果,此剪枝威力较大
                                                                                  
            if(!used[j]&&l>=sticks[j])   //剪枝3:最简单的剪枝,要拼成一根大棍子还需要的长度L>=当前小棍子长度,才能选用                           
            {
                l-=sticks[j];
                used[j]=1;
                if(dfs(j,l,t))return true;
                    
                l+=sticks[j];
                used[j]=0;
                
                if(sticks[j]==l)    //剪枝4:威力巨大的剪枝,程序要运行到此处说明往下的搜索失败,若本次的小棍长度刚好填满剩下长度,但是后
                    break;           //面的搜索失败,则应该返回上一层
            }
        }
    }
    return false;
}
bool cmp(const int a, const int b)
{ 
    return a>b; 
}
int main()
{
    while(cin>>n&&n)
    {
        int sum=0;
        for(int i=0;i<n;++i)
        {
            cin>>sticks[i];
            sum+=sticks[i];
            used[i]=0;
        }
        
        sort(sticks,sticks+n,cmp);   //剪枝5:从大到小排序后可大大减少递归次数
        
        bool flag=false;
        for(len=sticks[0];len<=sum/2;++len)   //剪枝6:大棍长度一定是所有小棍长度之和的因数,且最小因数应该不小于小棍中最长的长度
        {
            if(sum%len==0)     
            {
                if(dfs(0,len,sum))
                {
                    flag=true;
                    cout<<len<<endl;
                    break;
                }
            }
        }
        if(!flag)
            cout<<sum<<endl;
    }
    return 0;
}
 

dfs+剪枝~剪枝是关键~

主要的剪枝有:

1.从大到小排序,相同的长度如果已验证过不行就直接跳过;

2.范围从木棍最大长度maxx到总长tot,maxx和tot在输入的时候就记录下.

  • 5
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

深度学习推荐算法

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值