洛谷 P1120 小木棍 [数据加强版](深搜)

传送门

题目描述

乔治有一些同样长的小木棍,他把这些木棍随意砍成几段,直到每段的长都不超过50。

现在,他想把小木棍拼接成原来的样子,但是却忘记了自己开始时有多少根木棍和它们的长度。

给出每段小木棍的长度,编程帮他找出原始木棍的最小可能长度。

输入输出格式

输入格式:

输入文件共有二行。

第一行为一个单独的整数N表示砍过以后的小木棍的总数,其中N≤65

第二行为N个用空个隔开的正整数,表示N根小木棍的长度。

输出格式:

输出文件仅一行,表示要求的原始木棍的最小可能长度

输入输出样例

输入样例:

9

5 2 1 5 2 1 5 2 1

输出样例:

6

题解

深搜加剪枝不解释,具体做法看代码注释


Code:

#include
#include
#include
#include
using namespace std;

int n,tot=0,sum=0;
int a[70];
bool v[70],bj=false;

bool cmp(int x,int y)
{
    return x>y;
}

bool dfs(int m,int l,int len,int last) //m表示当前已经搜到了多少根木棍,l表示当
{                                      //前拼接的木棍离目标长度的距离,len表示目
    if(l==0 && m==tot) return true;    //标长度,last表示上一根搜到的木棍的编号 
	if(l==0) l=len;                    //如果上一根木棍已拼接完成,则重新开始 
    int t=1;
    if(l!=len) t=last;       //若l!=len,则说明这不是当前木棍的第一根小木棍 
    for(int i=t;i<=tot;i++)  //而排在上一根小木棍前的小木棍就不需要考虑 
    {
        if(!v[i] && a[i]<=l)
        {
            if(i>1 && !v[i-1] && a[i]==a[i-1]) continue; 
			//如果上一根小木棍的长度与这一根一样,且上一根没有用过,则这一根也不考虑 
            v[i]=true;       //标记为没有用过 
            if(dfs(m+1,l-a[i],len,i)) return true;
            else
            {
                v[i]=false;
                if(l==len || l==a[i]) return false;
				//l==len是指如果搜索了一大圈都不成功,则这不能作为答案
				//l==a[i]是指如果着一根长度正适合的都不能用到,则其他的不考虑 
            }
        }
    }
    return false;
}

int main()
{
    memset(v,false,sizeof(v));
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        int x;
        scanf("%d",&x);
        if(x>50) continue; //滤掉长度大于50的木棍 
        a[++tot]=x;
        sum+=x;
    }
    sort(a+1,a+1+tot,cmp);     //按照长度从大到小排序,由于每一根木棍都要用到,
                               //所以我们从最不好解决的,也就是最长的开始搜
	for(int i=a[1];i<=sum/2;i++) //枚举原始木棍的长度,如果枚举到sum/2都没有
    {                            //答案,则只能拼成一根木棍 
        if(sum%i!=0) continue; 
        if(sum%2==1 && i%2==0) continue;
        if(dfs(0,i,i,0))
        {
            printf("%d",i);
            bj=true;
            return 0;
        }	
    }
    if(!bj) printf("%d",sum);
}

你可能感兴趣的:(搜索)