P1120 小木棍 [数据加强版](dfs+6重优化)

P1120 小木棍 [数据加强版]

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

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

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

输入格式
共二行。

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

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

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

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

输入输出样例
输入 #1
9
5 2 1 5 2 1 5 2 1
输出 #1
6
题意:给定N条小木棍的长度,将其组成几根长度相同的小木棍,要求出组成小木棍的最小长度。
思路:用dfs枚举所有可能找出最小的即可,然而这道题最重要的不是找思路,而是给dfs剪枝,6重优化缺一不可,缺一就会超时,好好学好好看。ps:本蒟蒻看了题解后写的代码,超时到宇宙爆炸。(不要忘记管理员的备注哦,不然会死的很惨很惨).
屁话少说上代码:

#include <bits/stdc++.h>
using namespace std;
int a[70],n=0,sum=0,vis[70];
inline int read()//常规操作快读.
{
    int x=0,f=1;
    char c;
    c=getchar();
    while(!isdigit(c))
    {
        if(c=='-')f=-1;
        c=getchar();
    }
    while(isdigit(c))
    {
        x=(x<<3)+(x<<1)+c-'0';
        c=getchar();
    }
    return f*x;
}
void dfs(int k,int num,int x,int last)//k为已拼好木棍根数,num为当前正在拼的木棍长度,x为预期单根长度,last为上根木棍的标号.
{
    if(num==x)//拼成一根,转拼下一根.
    {
        k++;
        last=0;//last置0,又从长木棍开始拼凑.
        num=0;
    }
    if(k*x==sum)//完成目标输出后直接退出程序,减少不必要的逐层退出,
    {
        printf("%d",x);
        exit(0);
    }
    for(int i=last; i<n; i++)
    {
        while(a[i]+num>x&&i<n)//优化1:剪去下一跟长度大于未完成木棍的小木棍.
            i++;
        if(vis[i]==0)
        {
            vis[i]=1;
            dfs(k,num+a[i],x,i+1);
            vis[i]=0;
            if(num+a[i]==x||num==0)//优化2:最重要也是最难理解的,将会在题目尾部备注.
                break;
            while(a[i+1]==a[i]&&i+1<n)//优化3:既然该长度配对失败,下一根相同长度也不可能成功.
                i++;
        }
    }
}
int cmp(int a,int b)
{
    return a>b;
}
int main()
{
    int x,i,T,max1=-1;
    T=read();
    for(i=1; i<=T; i++)
    {
        x=read();
        if(x<=50)//管理员的话不听会WA的很惨很惨很惨很惨...
        {
            a[n++]=x;
            sum+=x;
            max1=max(max1,x);
        }
    }
    sort(a,a+n,cmp);//优化4:将长度从大大小排列,从大的开始拼凑,因为小木棍比大木棍更加灵活,可选择性更高.
    for(i=max1; i<=sum; i++)//优化5:原木棍长度必定大于等于砍断后最长木棍长度,小于总长度.
    {
        if(sum%i==0)//优化6:总长度比被单根木棍长度整除.
        {
            dfs(0,0,i,0);
        }
    }
    return 0;
}

讲讲代码中备注的事,干脆借鉴大佬的解释:

当前长棍剩余的未拼长度等于当前木棍的长度时,这根木棍在最优情况下显然是拼到这(如果用更多短木根拼完剩下的这段,把这根木棍留到后面显然不如把更多总长相等的短木棍扔到后面)。如果在最优情况下继续拼下去失败了,那肯定是之前的木棍用错了,回溯改即可。
 当前长棍剩余的未拼长度等于原始长度时,说明这根原来的长棍还一点没拼,现在正在放入一根木棍。很明显,这根木棍还没有跟其它棍子拼接,如果现在拼下去能成功话,它肯定是能用上的,即自组或与其它还没用的木棍拼接。但继续拼下去却失败,说明现在这根木棍不能用上,无法完成拼接,所以回溯改之前的木棍。
大佬云集的题解链接
Bye!

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值