AcWing
  • 首页
  • 课程
  • 题库
  • 更多
    • 竞赛
    • 题解
    • 分享
    • 问答
    • 应用
    • 校园
  • 关闭
    历史记录
    清除记录
    猜你想搜
    AcWing热点
  • App
  • 登录/注册

AcWing 167. 木棒(Python版代码)    原题链接    中等

作者: 作者的头像   小镇做题家XigMa ,  2024-02-12 22:32:18 ,  所有人可见 ,  阅读 42


1


题目链接:木棒(AcWing 167)

题目描述

乔治拿来一组等长的木棒,将它们随机地砍断,使得每一节木棍的长度都不超过 50
个长度单位。

然后他又想把这些木棍恢复到为裁截前的状态,但忘记了初始时有多少木棒以及木棒的初始长度。

请你设计一个程序,帮助乔治计算木棒的可能最小长度。

每一节木棍的长度都用大于零的整数表示。

输入格式

输入包含多组数据,每组数据包括两行。

第一行是一个不超过 64 的整数,表示砍断之后共有多少节木棍。

第二行是截断以后,所得到的各节木棍的长度。

在最后一组数据之后,是一个零。

样例

9
5 2 1 5 2 1 5 2 1
4
1 2 3 4
0

结果

6
5

解题思路

我们可以枚举木棒可能的长度length,然后使用采用dfs判断某个可能的长度是不是答案。按照题意可知木棒长度>=max(所有木棍长度)且<=sum(所有木棍的长度),此外依照题意还有一个约束条件,就是sum(木棍长度) % length == 0,因为不为0就无法将所有木棍都分好组,如如下代码(sa为木棍总长):

for length in range(a[0], sa + 1):
        if sa % length != 0: continue
        if dfs(0, 0, 0):#判断该长度是否可以
            print(length)
            break#由于只要最小值因此找到第一个答案就退出

当length固定时可知木棒总共有 sa // length 根(对它们编号,从0开始)
我们在dfs可以枚举哪些木 棍在哪些木 棒当中,即dfs(u, s, start) 中u的含义为当前在枚举编号为u的木 棒可能包含哪些木 棍,s表示已知的包含在u中的木 棍总长, start表示此次应该从编号为start的木 棍开始枚举(这点下方2有解释),

考虑以下方法对解空间树进行剪枝

1、优化搜索顺序:从大到小枚举(即把木 棍长度降序排序 )
2、排除等效冗余(比如:u包含编号0,2,3等价于u包含编号3,0,2),为了避免这种“等价”的情况我们应该按照“组合方式”枚举,这就是为什么dfs有一个start参数
3、当枚举到某根木 棍失败了,我们应该不枚举与它相同的木 棍(结果不变,但是可以少搜索很多空间)
4、当我们枚举某个木 棒的第一个木 棍的编号返回False时,直接返回False(原因太长不好讲,见y总视频),最后一个同理

Python代码

from sys import setrecursionlimit
setrecursionlimit(1000010)
def dfs(u, s, start):
    if u * length == sa: return True#递归出口
    if s == length: return dfs(u + 1, 0, 0)#递归出口
    i = start
    while i <= n - 1:
        if not st[i]:#第i个没用过
            if s + a[i] <= length:#可行性剪纸
                st[i] = True#标记用过
                if(dfs(u, s + a[i], i + 1)): return True
                st[i] = False#回溯

                j = i #采用剪枝方法3
                while j < n and a[i] == a[j]: j += 1
                i = j - 1

                #采用剪枝方法4    
                if s == 0: return False#第一个
                if s + a[i] == length: return False#最后一个
        i += 1
    return False

while True:
    n = int(input())
    if n == 0: break
    a = list(map(int, input().split()))
    a.sort(reverse = True)#优化搜索顺序
    sa = sum(a)
    st = [False] * n#标记某根木棍是不是被用过
    for length in range(a[0], sa + 1):
        if sa % length != 0: continue
        if dfs(0, 0, 0):#判断该长度是否可以
            print(length)
            break#由于只要最小值因此找到第一个答案就退出

0 评论

App 内打开
你确定删除吗?
1024
x

© 2018-2025 AcWing 版权所有  |  京ICP备2021015969号-2
用户协议  |  隐私政策  |  常见问题  |  联系我们
AcWing
请输入登录信息
更多登录方式: 微信图标 qq图标 qq图标
请输入绑定的邮箱地址
请输入注册信息