poj 1011 Sticks(dfs+剪枝)

该博客介绍了如何使用深度优先搜索(DFS)结合剪枝策略解决POJ 1011 Sticks问题。博主探讨了如何通过规定搜索顺序和避免等长小木棒的重复来优化搜索过程,从而找到最小可能的原始棍子长度。通过实例和剪枝策略的解释,博主展示了如何在不完全明确剪枝细节的情况下,依然能够实现高效的解决方案并获得AC(Accepted)结果。
摘要由CSDN通过智能技术生成

poj 1011 Sticks(dfs+剪枝)
总时间限制: 1000ms 内存限制: 65536kB

描述
George took sticks of the same length and cut them randomly until all parts became at most 50 units long. Now he wants to return sticks to the original state, but he forgot how many sticks he had originally and how long they were originally. Please help him and design a program which computes the smallest possible original length of those sticks. All lengths expressed in units are integers greater than zero.

输入
The input contains blocks of 2 lines. The first line contains the number of sticks parts after cutting, there are at most 64 sticks. The second line contains the lengths of those parts separated by the space. The last line of the file contains zero.

输出
The output should contains the smallest possible length of original sticks, one per line.

样例输入
9
5 2 1 5 2 1 5 2 1
4
1 2 3 4
0

样例输出
6
5

来源
Central Europe 1995

题意十分清晰,容易想到dfs,接下来无非如何剪枝的问题。
首先,剪枝思想不是很明晰显然,我是看了教学ppt才想到的,但是希望这回做到了这一次,以后能绷紧这根弦,遇到类似搜索马上想到这个剪枝。
注意到搜索重复情况十分多,不妨假设我们得到了一组解,那么我们这组解会被搜索到多次,以5 3 2 1 1 1这组数据为例,一个解为2+1,2+1,3,那么这个3可以第一次被搜到,也可以第二次第三次,而每一个2+1中2,1都不确定是哪一个2,1,而且也不确定是2+1还是1+2。那么搜索重复情况十分多了,我们从这一点入手剪枝。
剪枝1:
通过规定搜索合理顺序即可避免重复搜索,我们规定解必须由3,2+1这种情况搜出来(注意到最后一个2+1不必搜索了)。那么,也就是说优先安排大的小木棒在一根木棒的前面被搜到,这个很好理解。并且,前面的木棒的第一个小木棒一定比后面的第一个小木棒等长或长,因为可以规定这一种搜索顺序。也就是说先排序,再从前往后安排即可。
剪枝2:
避免等长小木棒的重复,比如某一个位置枚举过2,这个位置就不再枚举其它长为2的其它小木棒了。
教学ppt上讲述了4个剪枝,也许比我的剪枝更细腻,但是我没有仔细阅读教学ppt,我大致领会了它的精神,就是通过单调性规定搜索顺序剪枝。事实证明在这一思想指导下,即使剪枝不是很细致也可以0msAC。

Accepted    256kB   0ms 1573 B  G++
#define MAX_N 64
#define TEST
#undef TEST

#include<stdio.h>
#include<stdlib.h>

int n,sum_len,len[MAX_N+1],every_len,num;
bool used[MAX_N+1];
bool legal;

int compare(const void* e1,const void* e2)
{
    return *(int *)e2-*(int *)e1;
}

void check(int now,int now_len,int now_code)
{
    if (legal)
        return;
    #ifdef TEST
    if (every_len==3)
        printf("*%d %d %d\n",now,now_len,now_code);
    #endif
    if (now_len==every_len)
    {
        check(now+1,0,0);
        return;
    }
    if (now==num)
    {
        legal=true;
        return;
    }
    if (now_len==0)
    {
        for (int i=1;i<=n;i++)
            if (!used[i])
            {
                used[i]=true;
                check(now,len[i],i+1);
                used[i]=false;
                return;
            }
    }
    int j=0;
    for (int i=now_code;i<=n;i++)
    {
        if (now_len+len[i]<=every_len && !used[i] &&
            (len[i]!=len[j]))
        {
                j=i;
                used[i]=true;
                #ifdef TEST
                if (every_len==3)
                    printf("**%d %d %d i=%d\n",now,now_len,now_code,i);
                #endif
                check(now,now_len+len[i],i+1);
                used[i]=false;  
        }
    }
    return;
}

int main()
{
    #ifdef TEST
    freopen("input.txt","r",stdin);
    #endif
    while (scanf("%d",&n)&&n)
    {
        for (int i=1;i<=n;i++)
            scanf("%d",&len[i]);
        qsort(len+1,n,sizeof(int),compare);
        #ifdef TEST 
        for (int i=1;i<=n;i++)
            printf("%d ",len[i]);
        printf("\n");
        #endif
        sum_len=0;
        for (int i=1;i<=n;i++)
            sum_len+=len[i];
        for (int i=1;i<=n;i++)
            used[i]=false;
        for (every_len=len[1];every_len<=sum_len;every_len++)
            if (sum_len%every_len==0)
            {
                legal=false;
                num=sum_len/every_len;
                check(1,0,1);
                if (legal)
                    break;
            }
        printf("%d\n",every_len);
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值