微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

更改数组中的最小条目数,使任何 k 个连续项的总和为偶数

如何解决更改数组中的最小条目数,使任何 k 个连续项的总和为偶数

我们得到一个整数数组。我们必须更改这些整数的最小数量,但是我们希望这样,对于某些固定参数 k,数组中任何 k 个连续项的总和是偶数。 示例:

N = 8; K = 3; A = {1,2,3,4,5,6,7,8}

我们可以改变3个元素(4th,5th,6th) 所以数组可以是 {1,5,6,7,8} 然后

  • 1+2+3=6 是偶数
  • 2+3+5=10 是偶数
  • 3+5+6=14 是偶数
  • 5+6+7=18 是偶数
  • 6+7+7=20 是偶数
  • 7+7+8=22 是偶数

解决方法

有一个非常好的 O(n) 时间解决方案来解决这个问题,在高层次上,它是这样工作的:

  1. 认识到确定要翻转的项目归结为确定在要翻转的项目数组中重复的模式。
  2. 使用动态编程来确定该模式是什么。

这里是如何得出这个解决方案。

首先,一些观察。由于我们在这里只关心和是偶数还是奇数,我们实际上并不关心数字的确切值。我们只关心它们是偶数还是奇数。因此,让我们首先用 0(如果数字是偶数)或 1(如果它是奇数)替换每个数字。现在,我们的任务是让 k 个元素的每个窗口都有偶数个 1。

其次,转换数组后产生的 0 和 1 模式具有令人惊讶的形状:它只是数组前 k 个元素的重复副本。例如,假设 k = 5 并且我们决定数组应该从 1 0 1 1 1 开始。第六个数组元素必须是什么?好吧,在从第一个窗口移动到第二个窗口时,我们在窗口前面丢了一个 1,将奇偶校验更改为奇数。因此,我们必须让下一个数组元素为 1,这意味着第六个数组元素必须为 1,等于第一个数组元素。然后第七个数组元素必须是 0,因为在从第二个窗口移动到第三个窗口时,我们去掉了一个零。这个过程意味着我们对前 k 个元素的决定最终决定了整个最终的值序列。

这意味着我们可以通过以下方式重构问题:将 n 个项目的原始输入数组分成 n/k 个大小为 k 的块。我们现在被要求选择一个由 0 和 1 组成的序列,使得

  • 这个序列与 k 个项目的 n/k 个块在尽可能少的地方不同,并且
  • 序列有偶数个 1。

例如,给定输入序列

0 1 1 0 1 1 1 0 0 1 0 1 1 1 0 1 1 1

并且 k = 3,我们将形成块

0 1 1,0 1 1,1 0 0,1 0 1,1 1 0,1 1 1

然后尝试找到长度为 3 且其中包含偶数个 1 的模式,以便用该模式替换每个块需要最少的编辑次数。

让我们看看如何解决这个问题。让我们一点一点地工作。例如,我们可以问:使第一位为 0 的成本是多少?使第一位为 1 的成本是多少?使第一位为 0 的成本等于前面有 1 的块数,使第一位为 1 的成本等于前面有 0 的块数。我们可以计算出将每个位单独设置为零或一的成本。这给了我们一个这样的矩阵:

                       Bit #0   Bit #1   Bit #2   Bit #3     ...   Bit #k-1
---------------------+--------+--------+--------+--------+--------+----------
Cost of setting to 0 |        |        |        |        |        |        |
Cost of setting to 1 |        |        |        |        |        |        |

我们现在需要为每一列选择一个值,目标是最小化选择的总成本,受我们选择偶数位等于 1 的约束。这是一个很好的动态编程练习。我们考虑形式的子问题

如果您的选择具有从底行中选择的项目的奇偶校验 p,那么您可以从表格的前 m 列中获得的最低成本是多少?

我们可以将其存储在 (k + 1) × 2 表 T[m][p] 中,例如,其中 T[3][even] 是使用前三列可以实现的最低成本偶数项设置为 1,T[6][odd] 是使用前六列且奇数项设置为 1 可以实现的最低成本。这给出了以下循环:

  • T[0][even] = 0(使用零列没有任何成本)
  • T[0][odd] = ∞(如果不使用列,则不能将奇数位数设置为 1)
  • T[m+1][p] = min(T[m][p] + 将此位设置为 0 的成本,T[m][!p] + 将此位设置为 1 的成本)(或者使用零并保持相同的奇偶校验,或使用 1 并翻转奇偶校验)。

这可以在时间 O(k) 中进行评估,并且由此产生的最小成本由 T[n][even] 给出。您可以使用标准的 DP table walk 从这一点重建最优解。

总的来说,这是最终的算法:

create a table costs[k+1][2],all initially zero.

/* Populate the costs table. costs[m][0] is the cost of setting bit m
 * to 0; costs[m][1] is the cost of setting bit m to 1. We work this
 * out by breaking the input into blocks of size k,then seeing,for
 * each item within each block,what its parity is. The cost of setting
 * that bit to the other parity then increases by one.
 */
for i = 0 to n - 1:
    parity = array[i] % 2
    costs[i % k][!parity]++ // Cost of changing this entry

/* Do the DP algorithm to find the minimum cost. */
create array T[k + 1][2]
T[0][0] = 0
T[0][1] = infinity

for m from 1 to k:
    for p from 0 to 1:
        T[m][p] = min(T[m - 1][p]  + costs[m - 1][0],T[m - 1][!p] + costs[m - 1][1])

return T[m][0]

总的来说,我们在初始传递中执行 O(n) 工作,以计算将每个位独立设置为 0 的成本。然后我们在最后的 DP 步骤中执行 O(k) 工作。因此整个工作是 O(n + k),并且假设 k ≤ n(否则问题是微不足道的)成本是 O(n)

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。