Codility,第14课,任务TieRopes(https://codility.com/demo/take-sample-test/tie_ropes)。简而言之,问题是将正整数的列表A
划分为总和至少为K
的(连续)子列表的最大数量。
我只想出一个贪婪的解决方案,因为这是课程的名称。它通过了所有测试,但我不知道为什么它是最佳解决方案(如果它是最佳的)。
int solution(int K, vector<int> &A) {
int sum = 0, count = 0;
for (int a : A)
{
sum += a;
if (sum >= K)
{
++count;
sum = 0;
}
}
return count;
}
有人可以告诉我这个解决方案是否以及为何最佳?
答案 0 :(得分:4)
也许我天真或在这里犯了一些错误,但我认为看到算法确实是最优的并不是太难(虽然不是很明显)。
假设您具有列表的最佳分区,该分区具有最大数量的子列表。您可能拥有或不拥有列表中的所有元素,但由于向有效列表添加元素会生成有效列表,因此我们假设最初未分配给任何子列表的任何可能的“剩余”元素都被任意分配给其中一个相邻的子列表;所以我们有一个适当的列表最佳分区,我们称之为P1。
现在让我们考虑一下贪婪算法会产生的分区,比如说P2。 P2中的第一个子列表可能会发生两件事:
在1.中,您将在第一个子列表之后的下一个元素中重复推理。如果算法产生的每个后续子列表都等于P1中的子列表,则P1和P2将相等。
在2.你也会重复推理,但现在你至少有一个“额外”项目可用。所以,再次,下一个子列表可以:
2.1。获取P1中的下一个子列表 2.2。在P1中的下一个子列表之前结束。
重复一遍。因此,在每种情况下,您都将至少与P1一样多的子列表。这意味着,P2 至少与列表的任何可能分区一样好,特别是任何最佳分区。
这不是一个非常正式的演示,但我认为它是有效的。请指出您认为可能出错的任何内容。
答案 1 :(得分:1)
以下是导致正式证明的想法。
如果A
是B
的后缀,则A
的最大分区大小小于或等于B
的最大分区大小,因为我们可以扩展A
分区的第一个子列表,以包含新元素而不减少其总和。
贪婪解决方案中每个子列表的每个正确前缀总和小于K
。
没有任何意义,因为我们可以将缺少的元素添加到相邻的列表中(我认为我的问题的措辞已经排除了这种可能性,但无论如何我会说出来)
可以通过归纳进行形式证明,表明对于每个非负整数i
,存在一个最优解,它与每个i
子列表上的贪婪解一致。因此,当i
足够大时,唯一同意贪婪的解决方案是贪婪的,所以贪婪的解决方案是最优的。
基础i = 0
是微不足道的,因为任意最优解都可以。归纳步骤包括找到一个最佳解决方案,该解决方案与第一个i
子列表中的贪婪一致,然后缩小i+1
子列表以匹配贪婪解决方案(通过观察2,我们确实缩小了该子列表,因为它开始于与贪婪相同的位置;通过观察1,我们可以相应地扩展最优解的i+2
子列表。