问题描述
给定不同的数字列表,比如说;
list1 --> [2,4,2]
list2 --> [3,5]
list3 --> [4,4],list4 --> [5,5]
list5 --> [6]
求每个列表中数字的阶乘之和。 返回和的最大值。将总和计算为 Mod M,因为它们可能非常大。
例如,在上面的列表中,每个列表的总和,取 M=10^9 + 7,是
list1 --> (2!+4!+2!)%M = (2+24+2)%M = 26
list2 --> (3!+5!)%M = (6+120)%M = 126
list3 --> (4!+4!+4!+4!+4!+4!)%M = (24+24+24+24+24+24)%M = 144
list4 --> (5!+5!)%M = (120+120)%M = 240
list5 --> (6!)%M = 720
从上面的计算可以看出,list5
为必填列表。
我的问题是,当每个列表中的项目变大时,阶乘变得非常快,以至于 Mod 开始生效。 Mod生效后,我将无法找到最大数量,例如1000000008 % M = 1,but 10 % M = 10
。
如何有效地找到实际获得最大总和的列表?
约束
--> Size of each list is between 1(inclusive) and N(exclusive)
--> Each list contain numbers between 2(inclusive) and N(exclusive)
--> N is between 2(inclusive) and 10^6 (exclusive)
解决方法
在所有数组中找到最大元素,其中包含最大计数的元素。说这是p,重复r次。
所以这个数组的总和至少是 r * p!。
现在,假设其他数组的最大元素为 q,长度为 m。所以最大可能的阶乘总和是 m * q!。这个另一个数组可能有更大的总和需要什么? IE。值得检查吗?
如果 q = p 那么我们只需要 m > r 就值得检查了。
如果 q = p-1 那么我们需要 m * (p-1)! > r * p * (p-1)!所以 m > r * p。
如果 q = p-2 那么我们需要 m * (p-2)! > r * p * (p-1) * (p-2)!所以 m > r * p * (p-1)
等等...如果q = p-k,那么我们需要m > r * p! / (p-k)!
这是否有用取决于数组中的值。例如。如果单个数组的最大值为 N 重复一次,并且任何数组中的下一个最大值为 N-1 或更低,则具有 N 的数组必须是(或绑定)最大和数组(因为它需要N (N-1) 等于 N!)。
这有助于减少要检查的数组。除非所有数组中的 max 元素相对于 N 非常小,否则这可能会将您缩小到幸存者中的一组非常小的(最大值,最大值重复)值。例如。如果 p > sqrt(N) 那么幸存者可以有一个不小于 p-1 的最大值。
例如,如果 N 是 100 并且所有列表中的最大值是 11。假设这是一个只有一个元素的列表:[11]。嗯,11! = 39,916,800。自100*9!只是 36,288,000,我们可以丢弃最大元素
我们可以继续递归地使用这个想法来找到单个最高值列表。假设我们只有两个最大值:p 和 p-1。对于所有具有最大值 p 的列表,将它们的 ps 计数更改为零,并且对于每个去掉的 p,给出 p (p-1)s。接下来,找到具有最低计数 (p-1s) 的幸存列表,并从所有幸存列表中删除计数。然后重复这个过程。
对于相对于 N 非常小的值,我们将获得超过 2 个可能的最大值。您可以将其扩展到 3+,或者可能只是直接计算幸存者的阶乘总和,因为所有值都会很小。
---- 这是一个 O(num_lists * N) 版本----
假设有 L 个列表。
1. Sort each list in O(N) using bucket sort,total is O(L * N).
2. Find the max element across lists,use repetition to break ties.
3. Say this is p,repeated r times.
4. For each list (including this one): call remove(p,r,list)
5. Repeat for the new max element.
def remove(p,list):
while r > 0 and list.has(p):
list.remove(p)
r -= 1
if r > 0:
list.remove((p-1) * r * p)
如果列表出现无法解决的情况,remove 方法可以提前停止(如本答案的顶部部分)。
这总共是 O(L * N)。每次我们调用 remove 时,我们要么删除最多 N 个元素中的一个,要么将我们删除的内容减少最多 N 个元素中的 1 个,因此在耗尽列表之前每个列表最多可以调用 O(2N) 次。
---- Ruby 代码 ----
def find_max_fsum(lists,n)
sorted_lists = Hash.new { |h,k| h[k] = [0] * (n+1) }
lists.each_with_index do |list,i|
list.each do |elt|
sorted_lists[i][elt] += 1
end
end
max_val = n + 1
while max_val > 1 do
max_val -= 1
max_val_reps = 0
sorted_lists.values.each do |sorted_list|
max_val_reps = [max_val_reps,sorted_list[max_val]].max
end
if max_val_reps > 0
sorted_lists.each do |i,sorted_list|
success = remove(max_val,max_val_reps,sorted_list)
if !success
sorted_lists.delete(i)
end
end
end
end
sorted_lists.keys.each do |i|
print "max_fsum list: #{lists[i].to_s}\n"
end
end
def remove(max_val,sorted_list)
if max_val_reps <= sorted_list[max_val]
sorted_list[max_val] -= max_val_reps
return true
end
if (max_val_reps > sorted_list[max_val]) && max_val > 1
max_val_reps -= sorted_list[max_val]
sorted_list[max_val] = 0
return remove(max_val - 1,max_val_reps * max_val,sorted_list)
end
return false
end
find_max_fsum([[2,4,2],[3,5],[6,3,3],[4,4],[5,4]],6)
max_fsum list: [6,3]
find_max_fsum([[2,4]
,
每个正整数都有一个唯一的“基本阶乘”表示。该表示看起来像:a_1 * 1! + a_2 + 2! + a_3 * 3! + ...
与每个 a_n <= n
。这类似于以 10 为底的阶乘,但随着您在列表中向下移动,基数会增加。
如果你有一个非规范形式的阶乘表示,你可以通过从 1、2、3 等开始来解决这个问题,对于每个取 a_n mod (n+1)
作为余数,并调整下学期了。这与取 25 * 1 + 3 * 10 + 40 * 100
之类的东西并找出它是 5 * 1 + 5 * 10 + 4 * 1000
并因此在基数 10 中它是 4055
没有什么不同。
从您的列表中,您可以轻松生成阶乘表示,然后对其进行规范化。具有最大规范表示的那个就是您的答案。
这是您展示其工作原理的示例:
list1 --> [2,2]
2 * 2! + 1 * 4!
list2 --> [3,5]
1 * 3! + 1 * 5!
list3 --> [4,4]
6 * 4! = 1 * 4! + 1 * 5! (This is the first one we had to canonicalize.)
list4 --> [5,5]
2 * 5!
list5 --> [6]
6!
现在您只需对规范表示进行排序并发现 list5
最大,然后是 list4
,然后是 list3
,然后是 list2
,然后是 list1
。>