zoukankan      html  css  js  c++  java
  • 洛谷 P1120 小木棍 [数据加强版]解题报告

    P1120 小木棍 [数据加强版]

    题目描述

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

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

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

    输入输出格式

    输入格式:

    输入文件共有二行。

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

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

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

    输出格式:

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

    说明

    数据时限修改:

    -#17 #20 #22 #27 四组数据时限500ms

    -#21 #24 #28 #29 #30五组数据时限1000ms

    其他时限改为200ms(请放心食用)


    这是一个无比毒瘤的搜索题
    ------序

    我们枚举每一个长度(l),用爆搜检查这个长度(l)是否合法。

    关于毒瘤减枝:
    剪枝1:(l)如果不整除(sum),直接再见。这点很容易想到
    剪枝2:枚举上下界为(max{a_i}-sum/2),下界很容易想明白,而上界只是优化了下常数和减枝1一样的。
    剪枝3:将木棍从大到小排序进行枚举。这个减枝是以下所有减枝的前提。
    剪枝4:在凑成某根木棍时,从它上一次用的木棍编号的下一位开始枚举,因为反过来是一样的。
    剪枝5:如果某个长度和上一个长度一样且上一个长度的没有用,那么这个也不能用。
    剪枝6:维护一个后缀和数组(p[i]),如果当前长度加上最后所有能用的和还不够木棍原长,不能用。
    剪枝7:如果从当前编号枚举完整的棍子失败了以后,因为每个残棍都得用上,所以不行。
    剪枝8:这是一个贪心性减枝。如果当前长度加上枚举长度失败了以后,不用往后枚举了。
    因为哪怕后面有更小的能代替这个枚举长度,也不可能产生更优的结果了。因为更短的长度明显有更多选择。


    code:

    #include <cstdio>
    #include <algorithm>
    #include <cstdlib>
    using namespace std;
    const int N=70;
    int n,a[N],sum=0,s,len,ans=0,p[N],used[N];
    bool cmp(int x1,int x2)
    {
        return x1>x2;
    }
    
    void dfs(int last,int k,int l)//剪枝4
    {
        if(l==len)
        {
            l=0;
            k++;
            last=1;
        }
        if(k==s+1)
        {
            printf("%d
    ",len);
            exit(0);
        }
        for(int i=last;i<=n;i++)
        {
            if(!used[i]&&l+a[i]<=len)
            {
                if(a[i]==a[i-1]&&!used[i-1])//剪枝5
                    continue;
                if(l+p[i]<len)//剪枝6
                    continue;
                used[i]=1;
                dfs(i+1,k,l+a[i]);
                used[i]=0;
                if(l==0)//剪枝7
                    return;
                if(a[i]+l==len)//剪枝8
                    return;
            }
        }
    }
    
    int main()
    {
        scanf("%d",&n);
        int cnt=0;
        for(int i=1;i<=n;i++)
        {
            int b;
            scanf("%d",&b);
            if(b<=50)
            {
                a[++cnt]=b;
                sum+=b;
            }
        }
        n=cnt;
        sort(a+1,a+1+n,cmp);//剪枝3
        for(int i=n;i>=1;i--)
            p[i]=p[i+1]+a[i];
        for(int i=a[1];i<=sum/2;i++)//剪枝2
        {
            if(sum%i==0)//剪枝1
            {
                s=sum/i;
                len=i;
                dfs(1,1,0);
            }
        }
        printf("%d
    ",sum);
        return 0;
    }
    
    

    2018.5.22

  • 相关阅读:
    python模块----os
    shell脚本中给字符串添加颜色
    shell脚本的多线程
    Python读取xlsx文件
    修改docker出现中文字符出现乱码的问题
    Django基本命令
    C#排列数字组合方法
    SQL 参数化模糊查询
    iframe自适应高度
    Vue.js配合ajax绑定数据
  • 原文地址:https://www.cnblogs.com/butterflydew/p/9073390.html
Copyright © 2011-2022 走看看