有一组大小为 N <= 50
的正数。 此集合无法排序或更改!!!
S = {n1, n2, n3, ..., nN}
我需要在 S
组上拆分集合 M <= 25
:
Groups = {{n1, n2}, {n3, n4, n5...},...,{... nN}} -> len(Groups) = M
两个相邻组之间的最大差异应尽可能小。 差值是这样计算的:
diff = abs(sum(G1) - sum(G2)) = abs((n1 + n2) - (n3 + n4 + n5))
我可以生成大小为 N
的所有向量,其中只包含 M
非零数字(如 1
),但我将不得不考虑使用 50! / (25! * 25!)
进行暴力破解这是错误的:(
示例:
6, 13, 10, 2
case 1: {6}, {13, 10, 2} -> difference = 25
case 2: {6, 13}, {10, 2} -> difference = 5
case 3: {6, 13, 10}, {2} -> difference = 29
The best case is second -> difference = 5!
我可以用什么想法来解决这个问题?
答案 0 :(得分:2)
我结合动态规划和 A* 搜索的修改解决了这个问题。
import heapq
def best_division (numbers, pieces):
cumulative_sums = [0]
for number in numbers:
cumulative_sums.append(cumulative_sums[-1] + number)
# We need this to be somewhat bigger than the maximum sum.
# Making it + pieces/2 will result in a better
# expected_piece_size.
sum_bound = cumulative_sums[-1] + (pieces // 2)
# We expect our pieces to be somewhere close to this size.
expected_piece_size = (cumulative_sums[-1] + (pieces//2)) // pieces
# This data structure will include:
#
# By completed segments
# By start of segment
# By end of segment
# (lowest maximum difference, prev_start)
#
# It will actually be an array of dictionaries of dictionaries.
#
# It starts off empty.
best_path_info = []
# The queue is a minimum priority queue.
#
# [
# lowest maximum difference,
# abs(sum of segment - expected_piece_size),
# count_pieces,
# start,
# end,
# prev_start
# ]
#
# This causes us to first look at the lowest maximum difference,
# and then at the segment sum being close to the expected.
#
# It also means we can trace backwards once we have an answer.
queue = []
for i in range(len(cumulative_sums)):
heapq.heappush(
queue, [
0,
abs(cumulative_sums[i] - expected_piece_size),
0,
0,
i,
None
])
while len(queue):
max_diff, _, count_completed, start, end, prev_start = heapq.heappop(queue)
if count_completed + 1 == pieces: # The current piece is not in the count
if end == len(numbers):
answer = [numbers[start:end]]
end = start
start = prev_start
while start is not None:
answer.append(numbers[start:end])
count_completed -= 1
prev_start = best_path_info[count_completed][start][end][1]
end = start
start = prev_start
### RETURN HERE ###
return list(reversed(answer))
else:
continue # Not a solution
if len(best_path_info) <= count_completed:
best_path_info.append({})
path = best_path_info[count_completed]
if start not in path:
path[start] = {}
path = path[start]
if end not in path or max_diff < path[end][0]:
path[end] = (max_diff, prev_start)
for next_end in range(end, len(cumulative_sums)):
this_sum = cumulative_sums[end] - cumulative_sums[start]
next_sum = cumulative_sums[next_end] - cumulative_sums[end]
next_max_diff = max(max_diff, abs(next_sum - this_sum))
heapq.heappush(
queue, [
next_max_diff,
abs(next_sum - expected_piece_size),
count_completed + 1,
end,
next_end,
start
])