深搜剪枝——小木棍

论坛 期权论坛 脚本     
匿名网站用户   2020-12-19 21:55   11   0

问题 C: 【例题3】小木棍

时间限制:1 Sec内存限制:128 MB
提交:7解决:5
[提交][状态][讨论版][命题人:add_cy]

题目描述

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

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

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

输入

输入文件共有二行。

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

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

输出

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

样例输入

9

5 2 1 5 2 1 5 2 1

样例输出

6

从最优性方面:

1.设所有木棍长度和为sum,那么原长度(也就是需要输出的长度)一定能够被sum整除,这样得到的木棍根数才是整数

2.木棍原来的长度一定不小于所有木棍中最长的那根

综上两点,可以确定原木棍的长度len在最长木棍的长度maxlen和sum之间取值,且sum能被len整除。所以在搜索原木棍的长度时,可以从砍过以后所有木棍中最长的长度开始,每次增加长度后,必须能整除sum。这样可以有效优化程序。

从可行性方面:

1.短木棍更加灵活,长木棍受到的限制更大,所以可以对输入的所有木棍按长度从大到小排序。

2.在砍断后的排好序的木棍中,当用木棍i拼合原始木棍时,可以从i+1的木棍开始往后搜,因为i前面的木棍已经用过了

3.从当前最长长度的木棍开始搜,如果拼不出当前设定的原木棍长度len则直接返回,换一个原始木棍长度len

4.相同长度的木棍不要搜索多次.用当前长度的木棍搜下去得不出结果时,用一支同样长度的还是得不到结果,所以可以提前返回

5.判断搜到的几根木棍组成的长度是否大于原始长度len,如果大于没必要搜下去,可以提前返回

6.判断当前剩下的木棍根数是否够拼成木棍,如果不够,肯定拼合不成功,直接返回

7.找到结果后,在能返回的地方马上返回到上一层的递归处

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
using namespace std;
int n,m,len,sum=0,maxlen=0,flag;
int a[61],visit[61];
int cmp(int a,int b)
{
    return a>b;
}
void dfs(int k,int last,int rest)//k为第k根木棍,last为第k根上一节木棍编号;rest为第k根木棍还需要的长度
{
    int i;
    if(k==m){flag=1;return;}//木棍根数达到要求,剪枝
    if(rest==0)开始下一根木棍
    {
        for(i=0;i<n;i++)//查找一个未访问过的木棍节,剪枝
        if(!visit[i]){visit[i]=1;break;}
        dfs(k+1,i,len-a[i]);
    }
    for(i=last+1;i<n;i++)//当前剩下的木棍节,可行性剪枝2
    {
        if(!visit[i]&&a[i]<=rest)//木棍节为被访问过并且比需要的木棍长度短,可行性剪枝5
        {
            visit[i]=1;//置当前的木棍节为已使用
            dfs(k,i,rest-a[i]);
            visit[i]=0;//回溯
            int j=i;
            while(i<n&&a[i]==a[j])i++;//防止相同长度的木棍搜索多次,可行性剪枝4
            if(i==n-1)return;
        }
    }
}
int main()
{
    cin>>n;
    for(int i=0;i<n;i++)//依次读入每根木棍的长度
    {
        cin>>a[i];
        sum+=a[i];//求所有木棍的长度和
        if(a[i]>maxlen)maxlen=a[i];//求最长木棍即枚举的最小值
    }
    sort(a,a+n,cmp);//对木棍按从大到小排序,可行性剪枝1
    for(int i=maxlen;i<=sum;i++)//枚举原始木棍的长度,最优性剪枝
    {
        if(sum%i==0)//sum能被原木棍长度len整除
        {
            memset(visit,0,sizeof(visit));
            visit[0]=1;
            len=i;//保存每根木棍的长度
            m=sum/i;//记录木棍的根数
            flag=0;
            dfs(1,0,len-a[1]);
            if(flag)
            {
                cout<<len<<endl;
                break;
            }
        }
    }
    return 0;
}

分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:1136255
帖子:227251
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP