POJ 1011 Sticks

本文讨论了一个关于小棍长度拼接的问题,提出了从小到大枚举可行长度并利用DFS算法解决的方法,包括剪枝优化策略以提高效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目大意:给你n及n个小棍的长度,让你求出一个最小长度len,使n个小棒能无重复且无遗漏的拼接成若干根长度为len的木棒。

思路:对可行长度进行枚举,通过DFS判断该长度是否可行。由于要求最小可行解,所以从小到大枚举。

对于这里的DFS,有许多可以剪枝和优化的地方,值得学习:

(1)将棒的长度从大到小排列。事实上,如果按小棍长度从大到小进行测试,如果某长度恰好能填满可行解len,那么如果使用更长的小棍,长度显然会超过len,显然不行。如果使用更短的小棍,即使存在某个也能填满len的方案,该方案也不比使用长小棍更有可能获得可行解。

(2)令小棍总长度sum_len,当前枚举长度为test_len,如果sum_len mod test_len != 0,则剪枝。

(3)如果已经找到一个可行解,则对以后的所有情况剪枝。

(4)在填充len时,如果前一个小棍没有用过且当前小棍与前一个小棍长度相等,则剪枝。这里是防止重复测试。

(5)如果第一根小棍不能拼接成len,则剪枝。这个剪枝威力巨大。。我从TLE直接AC了。

事实上可能还有别的剪枝,因为我的程序运行了16MS,还没有到0MS秒杀的地步。而且我的程序对某些变态数据也有心无力。

变态数据:

64
40 40 30 35 35 26 15 40 40 40 40 40 40 40 40 40 40 40 40 40 40

40 40 43 42 42 41 10 4 40 40 40 40 40 40 40 40 40 40 40 40 40

40 25 39 46 40 10 4 40 40 37 18 17 16 15 40 40 40 40 40 40 40

40

答案为:454。

运行时间:5.5s+

 

#include <iostream>
#include <algorithm>

using namespace std;

struct Stick
{
    int len;
    bool have_used;
}stick[80];

int num,sum_len,max_len,piece_num,piece_len;
bool flag;

bool compare(struct Stick a,struct Stick b)
{
    return a.len>b.len;
}

void dfs(int pos,int len_now)
{
    int i;
    if (flag)
        return ;
    if (piece_num==0)
    {
        flag=true;
        return ;
    }
    if (len_now==piece_len)
    {
        piece_num--;
        dfs(0,0);
        piece_num++;
        return ;
    }
    for (i=pos;i<num;i++)
    {
        if (flag)
            return ;
        if (len_now+stick[i].len>piece_len)
            continue;
        if (stick[i].have_used)
            continue;
        if (i>0 && stick[i-1].have_used==false && stick[i].len==stick[i-1].len)
            continue;
        stick[i].have_used=true;
        dfs(i+1,len_now+stick[i].len);
        stick[i].have_used=false;
        if (pos==0)
            return ;
    }
}

int main()
{
    int i;
    while (scanf("%d",&num)==1 && num!=0)
    {
        for (i=0,sum_len=0,max_len=-1;i<num;i++)
        {
            scanf("%d",&stick[i].len);
            stick[i].have_used=false;
            sum_len+=stick[i].len;
        }
        sort(stick,stick+num,compare);
        max_len=stick[0].len;
        for (i=max_len,flag=false;i<=sum_len && flag==false;i++)
        {
            if (sum_len%i!=0)
                continue;
            piece_len=i;
            piece_num=sum_len/i;
            dfs(0,0);
        }
        printf("%d\n",piece_len);
    }
    return 0;
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值