选择N个数据包中的M个,使它们的总和为K的最小倍数。

4
我在Codechef上看到了这个程序。 有N个包装袋,每个包装袋里面都有一些糖果。(例如:第一个包装袋里面有10个,第二个包装袋里面有4个,以此类推) 我们需要从中恰好选择M个包装袋(M≤N),使得其中的所有糖果数量能够被K整除。 如果存在多个解决方案,则输出其中糖果数量最少的那个。
我认为它与子集求和问题相似,但那是NP难题。所以将需要指数级时间。
我不需要完整的解决方案,但希望能得到一个算法。我已经思考了两天,但无法得出正确的逻辑。
1 ≤ M ≤ N ≤ 50000,1 ≤ K ≤ 20 每个包装袋中的糖果数量 [1,10^9]

1 ≤ M ≤ N ≤ 50000,1 ≤ K ≤ 20。 - dejavu
1
@AndroidDecoded问题的标题不太好,请修正。例如,“选择M个N数据包,使得它们的和是K的最小倍数”。 - James Waldby - jwpat7
完成了。谢谢。是的,标题不好。 - dejavu
2个回答

1

数据包包含原始数据包。

k分成p = 1, 2, ..., m个数字的和,这些数字>= 1< k(有O(2^k)这样的分区)。对于每个分区,遍历数据包并添加那些余数模k是分区元素之一的数字,然后从分区中删除该元素。同时保持最小和,并更新全局最小值。请注意,如果m > p,您还必须有m - p个零。

你可能认为这是O(2^k * n),速度太慢了,但实际上如果你保持num[i] = 有多少个数字满足 packets[i] % k == i,那么你就不必为每个分区迭代packets数组,这样时间复杂度就变成了O(2^k + n)。为了处理最小和的要求,你可以保持num[i] = 具有 packets[i] % k == i 的数字列表,这将允许你始终选择有效分区中最小的数字。


第一行没看懂。你说的“分区k”是什么意思?我怎么才能分区k? - dejavu
OEIS链接所示,从维基百科链接中可以看出,分区数的增长速度比2^K慢得多。例如,numbpart(20)为627。该方法在K大几倍时可能更实用。例如,pari/gp语句for(i=1,9,print(numbpart(i*10)))显示,在x=10, 20, 30, ... 90时,numbpart(x)分别为42、627、5604、37338、204226、966467、4087968、15796476、56634173。 - James Waldby - jwpat7

0

在看过M之后,我认为状态空间更大了,但问题仍然可解决:循环i从1到N,在每一步中跟踪哪些组合(结果对K取模,使用恰好m个数据包)是可达的,使用的数据包编号为1到i。利用i-1时的可达组合来计算i时的可达组合。 - mcdowella

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接