【问题标题】:Ctrick BIT unable to understandCtrick BIT 看不懂
【发布时间】:2014-09-02 18:27:41
【问题描述】:

http://www.spoj.com/problems/CTRICK/是spoj的问题 它指出

魔术师洗好一小包纸牌,将其正面朝下并执行以下程序:

  1. 最上面的牌被移到牌组的底部。新的顶牌面朝上放在桌子上。这是黑桃 A。

  2. 两张卡从上到下一次移动一张。下一张牌面朝上发到桌子上。黑桃二。

  3. 一次移动三张卡片……

  4. 这样一直持续到第 n 张也是最后一张牌变成黑桃 n。

如果魔术师事先知道如何排列卡片(并且知道如何进行假洗牌),这个令人印象深刻的技巧就会奏效。您的程序必须确定给定数量的卡片的初始顺序,1 ≤ n ≤ 20000。

输入

输入的第一行是一个正整数,表示要遵循的测试用例的数量。每个案例由包含整数 n 的一行组成。 输出

对于每个测试用例,输出一行,其中值 1 到 n 的正确排列,空格分隔。第一个数字显示包的顶牌,等等…… 示例

输入: 2

4

5

输出:

2 1 4 3

3 1 4 5 2

现在我能想到的唯一解决方案是使用队列并模拟该过程。 但这将是 O(n^2)。我阅读了 cmets,他们建议使用 BIT 的段树。 我知道分段树和 BIT,但无法理解如何在这个问题中实现它们。请提出一些方法来做到这一点。 谢谢

【问题讨论】:

  • 你有没有试过这个问题?你目前的判决是什么?

标签: algorithm tree segment-tree


【解决方案1】:

我不知道为什么这个问题应该与 BIT 或段树联系起来,但我使用简单的“O(N^2)”模拟解决了这个问题。

首先这个问题的时间限制是11sN == 20000。这表明O(kN) 解决方案可能会通过问题。我相信你认为这个k应该是N,因为简单的模拟需要这个,但是可以通过某种方式对其进行优化。

让我们看看当 N == 5 时如何构造序列:

Round 1, count 1 space starting from first space after last position:        _ 1 _ _ _
Round 2, count 2 spaces starting from first space after last position:       _ 1 _ _ 2
Round 3, count 3 spaces starting from first space after last position:       3 1 _ _ 2
Round 4, count 4 spaces starting from first space after last position:       3 1 4 _ 2
Round 5, count 5 spaces starting from first space after last position:       3 1 4 5 2

我们可以看到一个很好的模式:对于圆形i,我们应该从最后一个位置后的第一个空格开始计算i 空格,并在必要时回退。

然而,关键的一步是:经过几轮后,剩余的空间将小于要计算的空间。在这种情况下,我们可以通过mod 来节省时间!

例如,在上一个示例的第 4 轮中,我们只剩下 2 个空格,但要计算 4 个空格。如果我们数到 4,那就是浪费时间。 计数 4 步相当于从最后一个位置后的第一个空格开始计数 4 % 2 == 0 个空格。你可以自己验证这一点:)

因此,我们可以使用代码来模拟这个过程:

memset(ans, 255, sizeof(ans));
while (cur <= n)
{
    int i, cnt;
    int left = n - cur + 1; // count how many spaces left
    left = cur % left + 1; // this line is critical, mod to save time!

    for (i = pos, cnt = 0; ; ++i) // simulate the process
    {
        if (i > n) i = 1;
        if (ans[i] == -1) ++cnt;
        if (cnt == left) break;
    }

    ans[i] = cur;
    pos = i;

    ++cur;
}

【讨论】:

    【解决方案2】:

    如果你想使用芬威克树(BIT)来解决这个问题,仔细看看nevets发布的解决方案,特别是这部分(感谢nevets的绘图):

    Round 1, count 1 space  starting from first space after last position:       _ 1 _ _ _
    Round 2, count 2 spaces starting from first space after last position:       _ 1 _ _ 2
    Round 3, count 3 spaces starting from first space after last position:       3 1 _ _ 2
    Round 4, count 4 spaces starting from first space after last position:       3 1 4 _ 2
    Round 5, count 5 spaces starting from first space after last position:       3 1 4 5 2
    

    使用上述方法找到正确的空闲空间的时间复杂度为 O(N),因为我们必须遍历所有空间(总复杂度 O(N^2))。请注意,我们可以使用以下方法计算下一个位置:

    free(next_pos) = (free(current_pos) + next_number) mod free(total) + 1
    

    其中 free(x) 告诉我们有多少空闲空间可以到达(包括)一个位置。这不是 next_pos 的直接公式,但它告诉我们它需要满足什么,因此我们可以使用这些信息对其进行二分查找。

    剩下要做的就是进行可用空间计算,这就是 BIT 发挥作用的地方,因为它为我们提供了查询和更新的 O(log N) 时间复杂度。现在找到空闲空间的时间复杂度是O(log^2 N),总时间复杂度是O(N log^2 N)。

    关于运行速度:

    • 建议的方法需要 3.16 秒
    • 1.18s 使用队列旋转元素
    • 0.60s使用链表旋转
    • 0.02s 使用 BIT。

    我必须说我对速度的提升感到非常惊讶:-)

    附:如果您不确定如何使用 BIT,请通过将所有值更新 +1 来进行初始化。将插槽标记为已占用时,只需将其更新为 -1 即可。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-09-21
      • 2015-08-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-01-01
      • 2018-09-09
      相关资源
      最近更新 更多