小木棍 [数据加强版](DFS剪枝)

题目描述

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

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

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

输入输出格式

输入格式:

共二行。

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

(管理员注:要把超过 50 的长度自觉过滤掉,坑了很多人了!)

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

输出格式:

一个数,表示要求的原始木棍的最小可能长度

输入输出样例

输入样例#1:

9
5 2 1 5 2 1 5 2 1

输出样例#1:

6

题解

好久没有写过题了,终于有时间写了几道

这道题写搜索的过程其实不难,难就难在如何剪枝缩短时间

题目所求的是最小木棍长度,因为木棍长度是一样的,所以我们可以直接搜索答案

在输入的时候先累加木棍长度的总和,因为一根木棍也是符合条件的,这可能就是答案

(记得将木棍长度大于50的数据忽略)

接着对木棍长度进行一个从大到小的排序,因为答案肯定大于等于木棍长度的最大值

再对答案进行枚举,枚举过程中可以优化:如果总和除以答案有余数,易知答案错误

在深搜中还有两步剪枝,我在题解中有备注

#include
#include
#include
#include
using namespace std;
inline int read()
{
    int sum=0;
    char ch=getchar();
    while(ch>57||ch<48) ch=getchar();
    while(ch>=48&&ch<=57) sum=sum*10+ch-48,ch=getchar();
    return sum;
}
bool cmp(int a,int b) { return a>b; }
int n,mid,cnt,sum,res,ans;
int a[71],vis[71];
bool dfs(int len,int sta,int now) //dfs(剩余长度,根数,组数) 
{
    if(now==res) return 1; //如果 组数 与 当前情况下组数 相等证明方案可行 
    if(len==0) if(dfs(ans,1,now+1)) return 1; //如果剩余长度为0,可以开始下一组的组合 
    for(int i=sta;i<=cnt;++i)
    {
        if(!vis[i]&&a[i]<=len) //如果这一根尚未使用并且小于剩余长度才可以使用 
        {
            vis[i]=1;
            if(dfs(len-a[i],i+1,now)) return 1;
            vis[i]=0;
            if(len==ans||len==a[i]) break; // One Step
            //如果剩余长度=答案证明所剩一根都比它长,显然不成立 
            //如果剩余长度等于当前值,因为还有更小的木棍所以拼不完也不成立 
            while(a[i]==a[i+1]) ++i; //Two Step
            //如果这一根不成立,那么长度和它一样的肯定也不成立 
        }
    }
    return 0;
}
int main()
{
    n=read();
    for(int i=1;i<=n;++i)
    {
        mid=read();
        if(mid>50) continue;
        a[++cnt]=mid,sum+=mid;
    }
    sort(a+1,a+cnt+1,cmp);//从大到小排序 
    for(int i=a[1];i<=sum;++i)//从小到大枚举答案 
    {
        if(sum%i) continue;//有余数肯定不是正解 
        res=sum/i;//当前情况下组数 
        ans=i;
        if(dfs(ans,1,0)) //如果方案可行输出答案 
        {
            printf("%d\n",ans);
            return 0;
        }
    }
}

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