洛谷 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

原文地址:https://www.cnblogs.com/butterflydew/p/9073390.html