2

最近我遇到了一个似乎很有趣的游戏,它建议在大型数据集上同时实现两指针和前缀和技术。这是任务本身:

想象有一个长度为k的数组v ,其中k (k<=10**5) 表示由两个数字组成的条目数量:A(所谓的堆)和N(数量),例如:

k = 3
v = [[2, 2], [3, 2], [2, 1]]

A ( v[i][0]) 是值,N ( v[i][1]) 是给出该值的次数。换句话说,NA的频率。

现在,我们要做的是选择最小的 A,从列表的两端开始,从当前位置减去它,然后将它添加到后面的位置。然后我们继续这样做,直到中间只剩下两个或一个etries。换句话说,每一步我们选择最小的一堆,然后从两端减去它,直到剩下一两堆。

答案应该包括两行:第一行包含“1”或“2”,具体取决于剩余的桩数,第二行是结果本身。

为简化起见,我们可以将堆表示为如下所示的普通列表:

v = [2, 2, 3, 3, 2] 

我们从左端和右端(2)中选择最小值,减去它并添加到下一个值:

v = [0, 4, 3, 5, 0]

4 和 5 的最小值是 4,所以我们减去 4 得到两堆。

v = [0, 0, 11, 1, 0]

输出是:

2
11 1

我的代码是:

k = int(input())
a = []
for _ in range(k):
    A, N = map(int, input().split())
    x = [A] * N
    a += x


l = 0
r = len(a)-1
while r - l > 1:
    s = min(a[l], a[r])
    a[l] -= s
    a[r] -= s
    a[l+1] += s
    a[r-1] += s
    if a[l] == 0:
       l += 1
    if a[r] == 0:
       r -= 1

if a[r] == 0 or a[r]==a[l]:
    print(1)
    print(a[l])
      
else:
    print(2)
    print(a[l], a[r])

该解决方案在k = 10**5时未能达到时间限制(2 秒),因为它需要将数组转换为普通列表,并且几乎不使用前缀和。我确信有一个更快的解决方案,它带有前缀和、附加变量和存储一个给定的数组。对于如何加快此代码的任何建议,我将不胜感激。
为防止任何与语言相关的评论:该游戏适用于任何编程语言,因此时间限制不是专门针对 python 的问题。

4

1 回答 1

2

我不会给你答案,但我会给出一条推理出答案的路径。

诀窍不是更快地执行算法,而是在做更少的工作的同时找出应用算法的结果。为了使这更容易,我将用更慢的算法替换该算法,这恰好更容易推理。

而不是从两边减去较小的,加上下一个,让我们从每边减去1,加到下一个。所以你的例子将开始:

[2, 2, 3, 3, 2]
[1, 3, 3, 4, 1]
[0, 4, 3, 5, 0]
...

让我们开始推理。

首先假设我们A在一端,需要多少步才能将那一边推进?答案很简单A

如果我们A跟着k as 会发生什么?前进通过块,直到所有东西都堆在最后一个元素上

A + A+a + A+2a + ... A+(k-1)a
    = kA + (1 + 2 + ... + (k-1))a
    = kA + k(k-1)a/2

此时最后一个元素现在具有A + ka元素。

所以现在,在不扩展一个块的情况下,我们可以用一个简单的公式计算出通过它需要多少工作。我们也可以在另一边这样做。因此,在不扩展的情况下,我们可以开始消耗每一侧的整个块,直到我们在最后 2 或 3 个块遇到中间。然后我们必须小心,但是当另一方完成一个块时,我们可以准确地确定一个在哪里。然后我们可以将这些块分成更小的块元素并继续前进,直到我们在前进的前沿之间找到最多 3 个数组元素,它们将相遇。然后我们将原始算法应用几次以获得确切的答案。

但是您必须能够通过一次算术计算来遍历整个块,以使其足够快。

祝所有边界条件都正确!

于 2021-11-01T19:43:12.007 回答