【问题标题】:Count number of consecutive candies计算连续糖果的数量
【发布时间】:2016-10-23 12:28:22
【问题描述】:

我正在尝试解决一个定义如下的问题:

糖果由 1 到 1e6 之间的数字表示。一个人 如果他有糖果 1 到 k,则说他有一组 k 糖果。

例如如果一个人买了糖果 1、2 和 6,那么他有一套 2 糖果

给定 2 种类型的操作:吃 x 和买 x,其中 x 代表 糖果号。购买 x 只会将 x 的数量增加 1。吃x 只会将 x 的数量减少 1。

对于每个操作,回答问题,我现在拥有的那套糖果的大小是多少?

我正在努力寻找最有效的方法来做到这一点。我想到的解决方案描述如下:

让 count[i] 定义大小为 1 - N 的数组,其中 N 是可能的最大糖果数。 count[i] 存储我目前拥有的编号为 i 的糖果的数量。

让 Fenwick[i] 数组大小为 1 - N,其中 N 是可能的最大糖果数。这个数组用于构建一个 fenwick 树来存储我收藏中糖果的累积总和。此累积和不使用计数数组。累积总和计数 1 的数量(每个 1 表示我的收藏中存在糖果 x)。例如如果我有一套 5 颗糖果,那么从 1 到 5 的累积总和是 5。如果有一套 10 颗糖果,那么从 1 到 10 的累积总和是 10...

对于购买操作,如果我的收藏中还没有糖果 x,则从索引 x 开始的累积总和加 1(这由 fenwick 树处理)。否则,我将执行 count[x]++

对于eat操作,执行count[x]--。如果 count[x] 现在为 0,那么我从索引 x 开始的累积总和中减 1(这由 fenwick 树处理)。

现在解决了插入和删除的部分。困难的部分是如何获取当前集合中的糖果集的大小。

我尝试在 Fenwick 树中查询最大索引 i,从 1 到 i 的累积和等于 i,同时每次以 2 的幂递增查询索引。

我采用作为有效糖果集合的最大索引 j 和作为无效糖果集合的最小索引 k。然后从 j 循环到 k,在每次迭代时查询 fenwick 树。一旦循环遇到无效集合,中断并输出答案。

在我看来这是可行的。但是,这肯定不是一种有效的方法。有人能启发我更好的解决方案吗?提前致谢。

编辑(解决方案):

我的插入和删除方法是正确的。只是我以不正确的方式搜索糖果的集合。在这种情况下,我们想要最大的数 x,其中 query(x) = x(query(x) 给出从 1 到 x 的累积和)。所以我们可以使用二分查找来找到x的最大有效值(query(x) = x)。为了实现这一点,我们只需要保留一个额外的变量来跟踪 x 的最后一个值,其中 query(x) 给出了一个有效的集合。

解的复杂度:O(log^2(N))

【问题讨论】:

    标签: algorithm fenwick-tree


    【解决方案1】:

    这通常是一个二叉树结构。

    为简单起见,假设糖果的索引范围从02^k - 1,对于某个整数k。那么在每一刻,状态都由16数字表示,c[0]c[2^k - 1],其中c[i]是糖果i的数量。

    我们构造一棵二叉树如下:根节点P(0, 2^k)代表整个区间[0, 2^k);对于每个节点P(a, b) 使得b - a > 1,构造两个子节点P(a, (a + b)/2)P((a + b)/2, b)

    p(a, b)c[i]i 在区间[a, b) 中的最小值。显然我们有:

    • p(a, a + 1) = c[a];

    • p(a, b) = min{p(a, (a + b)/2), p((a + b)/2, b)} 如果b - a > 1

    构建了这个数据结构后,对于每个操作(加一或减一),我们可以在O(k) 步骤中从下到上更新数据。此外,还可以在O(k) 步骤中找到糖果组的大小。


    数据结构示例:

    让我们看一下k = 3的情况,这样就有c[0]c[7]。例如:

    c[0 .. 7] = {1, 3, 0, 4, 3, 2, 8, 1}

    然后树结构如下所示:

    p(0, 8) = 0
    |- p(0, 4) = 0
    |  |- p(0, 2) = 1
    |  |  |- p(0, 1) = 1
    |  |  |_ p(1, 2) = 3
    |  |_ p(2, 4) = 0
    |     |- p(2, 3) = 0
    |     |_ p(3, 4) = 4
    |_ p(4, 8) = 1
       |- p(4, 6) = 2
       |  |- p(4, 5) = 3
       |  |_ p(5, 6) = 2
       |_ p(6, 8) = 1
          |- p(6, 7) = 8
          |_ p(7, 8) = 1
    

    现在假设我们将1添加到数字c[2],这就是1,那么我们只需要更新数字p(2, 3)p(2, 4)p(0, 4)p(0, 8)

    【讨论】:

    • 那么 p(a, b) 表示我需要在每个节点中存储 2 个整数?
    • 不,p(a, b) 是一个附加到区间 [a, b) 的数字。我添加了一个示例来说明结构。
    • 哦。我知道了。这是一个段树吗?
    • Ja,它是(一种特殊情况)。
    • 谢谢!尽管这个答案并不完全正确,但至少数据结构的想法是正确的!我会将其标记为已接受。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-02-18
    • 1970-01-01
    • 2019-03-14
    • 1970-01-01
    • 1970-01-01
    • 2014-01-06
    相关资源
    最近更新 更多