Python数据结构与算法(poj):P01011——Sticks

OpenJudge - 01011:Sticks

        非常经典的一道dfs题目,很考验剪枝的能力。

        题目给出了一些不同长度的小木棍,要求我们求出能够将它们拼成若干相同长度,所需要的最小长度。显然最长的长度就是木棍长度之和,并且最小的长度肯定大于最长的木棍长度,因此我们只需要在最长的木棍长度到总长度的一半进行搜索即可。

        对于一个给定长度进行判断的方法是不难想的,从最长的木棍长度开始(相当于对木棍长度构成的数组进行了排序,从大到小),分别进行尝试,每拼到给定长度,就进行新的一次尝试(同时将之前尝试且能够拼到给定长度的木棍进行标记,之后不再使用),如果最后既不需要木棍去拼(刚好某一次尝试结束),也没有剩下的木棍,那么就说明这一给定长度是满足条件的。又由于我们从小到大进行尝试,所以直接输出就可以了。

        但是这样显然会超时!

        因此,我们需要剪枝。我使用了三个剪枝,分别如下:

        1、如果有两根木棍长度相同,而前一个木棍没有被使用,那么这一个木棍同样无法得到使用,因为每次遍历的时候,前一个永远是先被尝试的。

        用代码写是这样:(这里的nums数组存储木棍长度,used数组用于标记木棍的访问状态)

if nums[k-1] == nums[k] and not used[k-1]:
    continue

        2、如果我们进行尝试的时候,所使用的这根木棍长度恰好与为达到给定长度所需要的长度相等(也就是说使用了这根木棍就可以开始新尝试),亦或此时恰好开始一次新的尝试,得到的结果是False,那么就说明这个给定长度不满足条件。 

        用代码写是这样:(这里y是目前还需要多长才能结束一次尝试)

if y == l or nums[k] == y:
    return False

        3、如果我们选用的给定长度并非总长度的因子,那么它一定不满足条件。

        用代码写是这样:(这里total是总长度,p是给定长度)

if total % p:
    continue

由此,我们就可以写出在时间范围内的代码了~

def dfs(x, y, l):
    if x == y == 0:
        return True
    if y == 0:
        y = l
    for k in range(n):
        if nums[k] <= y and not used[k]:
            if k > 0:
                if nums[k-1] == nums[k] and not used[k-1]:
                    continue
            used[k] = True
            if dfs(x-1, y-nums[k], l):
                return True
            else:
                used[k] = False
                if y == l or nums[k] == y:
                    return False
    return False

while True:
    n = int(input())
    if n == 0:
        break
    nums = list(map(int, input().split()))
    nums.sort(reverse=True)
    total = sum(nums)
    for p in range(nums[0], total//2 + 1):
        if total % p:
            continue
        used = [False for _ in range(n)]
        if dfs(n, p, p):
            print(p)
            break
    else:
        print(total)

  • 5
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值