Sticks(经典深搜+剪枝)

#include 
#include 
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&&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<
经典搜索题,果然是到处充斥着剪枝才能过啊,我的代码离剪到极限还差很多
题目给出一大堆小棍子的长度,需要把他们拼成几根长度相等的大棍子,求大棍子的最短长度
 
 
#include 
#include 
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;j0&&(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>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<

dfs+剪枝~剪枝是关键~

主要的剪枝有:

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

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

你可能感兴趣的:(oJ练习(C++))