[CERC 1995] 小木棍

GitHub跳转原题关系图返回列表

枚举总长度的因数作为原木长度,用降序拼组 DFS 与失败剪枝判断可行性。

OJ: luogu

题目 ID: P1120

难度:提高+/省选-

标签:DFS剪枝回溯python

日期: 2026-07-16 20:10

题意

若干短木段来自若干根等长原木,求原木最小可能长度。

思路

候选长度至少是最长木段,并且必须整除总长度,按从小到大枚举。对固定目标,用 DFS 依次拼满每根原木。

木段降序能更早产生冲突。搜索中跳过同层相同长度;若一根新原木的第一段失败,或某段恰好补满后仍失败,换同层其他段也不可能挽救,立即回退。

Python 知识

  • sorted(generator, reverse=True) 直接生成降序木段。
  • used = [False] * n 配合闭包回溯,无需复制整个状态。
  • previous 跳过同层重复值,是处理重复元素组合搜索的常用写法。

代码

python
import sys


data = iter(map(int, sys.stdin.buffer.read().split()))
n = next(data)
pieces = sorted((next(data) for _ in range(n)), reverse=True)
total = sum(pieces)
used = [False] * n


def possible(target):
    def dfs(used_count, current, start):
        if used_count == n:
            return True
        if current == target:
            return dfs(used_count, 0, 0)
        previous = -1
        for i in range(start, n):
            length = pieces[i]
            if used[i] or length == previous or current + length > target:
                continue
            used[i] = True
            if dfs(used_count + 1, current + length, i + 1):
                return True
            used[i] = False
            previous = length
            if current == 0 or current + length == target:
                return False
        return False

    return dfs(0, 0, 0)


for answer in range(pieces[0], total + 1):
    if total % answer == 0 and possible(answer):
        print(answer)
        break

复杂度

最坏仍为指数级,但因数筛选、降序和三类剪枝使题目数据可通过;空间 O(n)O(n)

总结

组合搜索的速度往往取决于失败是否足够早:先放大段、去重、识别等价失败分支缺一不可。