非常经典的一道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)