Algorithm Ctrick位无法理解
这是spoj的一个问题 它说 魔术师洗牌一小包牌,将其正面朝下,然后执行以下步骤:Algorithm Ctrick位无法理解,algorithm,tree,segment-tree,Algorithm,Tree,Segment Tree,这是spoj的一个问题 它说 魔术师洗牌一小包牌,将其正面朝下,然后执行以下步骤: 顶部的卡被移动到包的底部。新的顶牌面朝上发到桌上。这是黑桃的王牌 两张牌一次从上到下移动一张。下一张牌面朝上发到桌上。这是两个黑桃 三张牌一次移动一张 这种情况一直持续到第n张牌和最后一张牌变成黑桃的第n张 如果魔术师知道如何事先安排牌(并且知道如何进行假洗牌),这个令人印象深刻的把戏就会奏效。您的程序必须确定给定数量的卡的初始顺序,1≤ N≤ 20000. 输入 输入的第一行是一个正整数,指示要遵循的测试用例
Thanx我不知道为什么这个问题应该与位树或段树联系起来,但我使用简单的“O(N^2)”模拟解决了这个问题 首先,此问题的时间限制为
11s
,并且N==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;
}
memset(ans,255,sizeof(ans));
而(curn)i=1;
如果(ans[i]=-1)++cnt;
如果(cnt==左)断开;
}
ans[i]=cur;
pos=i;
++cur;
}
如果您想使用Fenwick树(BIT)解决此问题,请仔细查看nevets发布的解决方案,尤其是这一部分(感谢nevets的绘图):
使用上述方法寻找正确的自由空间的时间复杂度为O(N),因为我们必须遍历所有空间(总复杂度为O(N^2))。请注意,我们可以使用以下公式计算下一个位置:
free(next_pos) = (free(current_pos) + next_number) mod free(total) + 1
其中free(x)告诉我们一个位置上有多少个可用空间。这不是next_pos的直接公式,但它告诉我们需要满足什么,所以我们可以使用此信息对其进行二进制搜索
剩下要做的唯一一件事就是进行自由空间计算,这就是BIT发挥作用的地方,因为它为查询和更新提供了O(logn)的时间复杂度。现在,查找可用空间的时间复杂度为O(log^2 N),总时间复杂度为O(N log^2 N)
至于行驶速度:
- 3.16s用于建议的进近
- 1.18s使用队列旋转元素
- 使用链表旋转0.60秒
- 0.02s使用一个位
注意:如果您不确定如何使用该位,请通过将所有值更新为+1来初始化。将插槽标记为已占用时,只需将其更新为-1即可。您尝试过该问题吗?你现在的判决是什么?
free(next_pos) = (free(current_pos) + next_number) mod free(total) + 1