Sticks (dfs经典剪枝)

George took sticks of the same length and cut them randomly until all parts became at most 50 units long. Now he wants to return sticks to the original state, but he forgot how many sticks he had originally and how long they were originally. Please help him and design a program which computes the smallest possible original length of those sticks. All lengths expressed in units are integers greater than zero.
InputThe input contains blocks of 2 lines. The first line contains the number of sticks parts after cutting, there are at most 64 sticks. The second line contains the lengths of those parts separated by the space. The last line of the file contains zero.
OutputThe output file contains the smallest possible length of original sticks, one per line.
Sample Input
9
5 2 1 5 2 1 5 2 1
4
1 2 3 4
0
Sample Output

65


题目分析  :DFS + 剪枝 ,本题限制时间为1000ms,剪枝不到位绝对超时,下面列出几条不可少的剪枝

1. 设有n根木条枚举能拼成的长度为sum/n~1,这里n必须是sum的约数否则无解,找到了则为最优解,因为均分的越多长度就越短

2.按长度从大到小排序,最长的一根长度必然小于sum/n,否则无解,从长的开始取,因为长的灵活度比较低,之后便于剪枝。

3.若搜索时某两根的长度相同,第一根没取那第二根也不会取

4.每次取的木条加上取之前的长度要小于sum/n

5.
这题则必须加这条剪枝 :如果当前长度cur为0,当前取的最长木条为stick[i],结果stick[i]拼接失败则我们可以直接退出搜索,当前情况无解,因为如果某次当前最长的木条没被选上,则之后木条数更少了它更不会被选上,因为我们是按照降序排列的


这里再给一组数据:
64
40 40 30 35 35 26 15 40 40 40 40 40 40 40 40 40 40 40 40 40 40
40 40 43 42 42 41 10 4 40 40 40 40 40 40 40 40 40 40 40 40 40
40 25 39 46 40 10 4 40 40 37 18 17 16 15 40 40 40 40 40 40 40 40

答案: 454


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

bool vis[65];
int tm[65];
int x;
int flag;
int n;
void dfs(int deep,int len,int num)    //deep用过木棒数,len当前选取到的长度,num当前小棒序号
{
    int i;
    if (flag) return ;
    if (len==0)
    {
          i=1;
        while(vis[i])  i++;            //找到未用的最长的棒子
        vis[i]=true;
        dfs(deep+1,len+tm[i],i+1);   //选取下一根
        vis[i]=false;    //如果本递归退出了,说明当前选择拼凑X的方案不合法;
        return ;     
    }
    if (len==x)
    {
       
        if (deep==n)    //全部选完了,且都能合成长度为x的方案
            flag=1;        //成功结束标记
        else
            dfs(deep,0,0);        //继续下一根拼凑
        return;
    }

    for (i=num;i<=n;i++)        //从比当前小的木棒开始选
    {
        if (!vis[i]&&len+tm[i]<=x)        // 没选过且 长度拼在一起不超过x
        {
         if (!vis[i-1]&& tm[i]==tm[i-1])  continue;//上一跟一样的都不选,这跟必然不用勾选  //most important cut
                vis[i]=true;
            dfs(deep+1,len+tm[i],i+1);        //继续选
            vis[i]=false;
            if (tm[i]+len==x)  return ; //由于是从大到小选取,如果这个刚好能凑成X的方案最终都不合法,那么必然无解.如果存在其他方案合法,例如较短的另外几根,但当前方案不合法,这是矛盾的
              if (flag) return ;
        }
    }                      
     if (flag) return ;   
}

bool cmp(int a,int b)
{return a>b;}
int main()
{
    while(cin>>n&&n)
    {
       
        int sum=0;
        int i;
        for (i=1;i<=n;i++)
        {
            scanf("%d",&tm[i]);
            sum+=tm[i];
        }
        sort(tm+1,tm+1+n,cmp);
       
        for (i=tm[1];i<=sum;i++)
        {
            if (sum%i) continue;
            flag=0;
            memset(vis,0,sizeof(vis));
            x=i;
            dfs(0,0,1);
            if (flag)
            {printf("%d\n",i);break;}
        } 

    }
    return 0;
   
}

你可能感兴趣的:(暴力回溯)