• 【PE806】Nim on Towers of Hanoi(汉诺塔游戏,生成函数)


    PE:Project Euler

    题意:

    汉诺塔游戏是如下的问题:有三根柱子,第一根柱子套有 n n n 个圆盘,圆盘从上往下半径递增。每次操作可以把套在某根柱子上的最上面的那个圆盘移到另一个柱子上。但需保证过程中每根柱子都始终满足大盘在下小盘在上。现要在最小的步数内,将这 n n n 个圆盘仍按从上往下半径递增的顺序,全部套在第三根柱子上。

    现在按最优策略进行汉诺塔游戏。过程中,设 a , b , c a,b,c a,b,c 是当前三根柱子上的盘子个数,若 a ⊕ b ⊕ c = 0 a\oplus b\oplus c=0 abc=0,那么让答案加等于当前的步数。求最后答案。

    n = 1 0 5 n=10^5 n=105

    题解:

    首先得知道汉诺塔游戏的最优策略是啥。设 n n n 个盘子的答案为 f n f_n fn

    考虑最大的那个盘子,即第 n n n 个盘子。显然存在一个时刻把它移到第三根柱子并固定下来永远不动。那么此时需要满足:第一根柱子只有第 n n n 个盘子、第二根柱子按半径递增套着剩下 n − 1 n-1 n1 个盘子、第三根柱子为空。

    那么我们需要把原来第一根柱子上的 n − 1 n-1 n1 个盘子全部移到第二根柱子,这个过程的步数下界是 f n − 1 f_{n-1} fn1(显然第一根柱子上的第 n n n 个盘子并不会使得步数在 f n − 1 f_{n-1} fn1 的基础上变得更少),而显然这个步数下界是可以达到的(忽略第 n n n 个盘子即可,因为它是全局最大的)。

    同理可以证明,最优方案是用 f n − 1 f_{n-1} fn1 步把第二根柱子上的 n − 1 n-1 n1 个盘子移到第三根柱子上。

    从而得到 f n = 2 f n − 1 + 1 = 2 n − 1 f_n=2f_{n-1}+1=2^n-1 fn=2fn1+1=2n1,以及最优策略(显然最优策略唯一,我们不可能在动 n − 1 n-1 n1 个盘子的时候动第 n n n 个盘子,这样会使答案变劣)。

    现在转到另一个问题:我们先考虑满足 a + b + c = n a+b+c=n a+b+c=n a ⊕ b ⊕ c = 0 a\oplus b\oplus c=0 abc=0 ( a , b , c ) (a,b,c) (a,b,c) 应满足什么条件。

    可以归纳证明,在 a ⊕ b ⊕ c = 0 a\oplus b\oplus c=0 abc=0 的前提下, a + b + c = n a+b+c=n a+b+c=n 过程中每次进位不超过 1 1 1。同时,可以根据 a + b + c a+b+c a+b+c 在某位上的值确定上一位具体往这一位进的是 0 0 0 还是 1 1 1。同时,若上一位进了 0 0 0,那么上一位必定是三个 0 0 0;若上一位进了 1 1 1,那么上一位 a , b , c a,b,c a,b,c 中必是两个 1 1 1 一个 0 0 0

    那么可以在 O ( 3 popc ⁡ ( n ) ) O(3^{\operatorname{popc}(n)}) O(3popc(n)) 的时间复杂度内枚举每一种 ( a , b , c ) (a,b,c) (a,b,c)

    又注意到若 ( a , b , c ) (a,b,c) (a,b,c) 在第 x x x 步出现了,那么对称地有 ( c , b , a ) (c,b,a) (c,b,a) 在第 2 n − 1 − x 2^{n}-1-x 2n1x 步出现。

    于是,只需对于某个 ( a , b , c ) (a,b,c) (a,b,c),知道它在过程中出现了多少次即可。

    考虑 DP,设 f a , b , c f_{a,b,c} fa,b,c 表示在 n = a + b + c n=a+b+c n=a+b+c 的汉诺塔游戏中, ( a , b , c ) (a,b,c) (a,b,c) 在过程中出现的次数。于是 f a , b , c = f c , b , a f_{a,b,c}=f_{c,b,a} fa,b,c=fc,b,a

    考虑 ( a , b , c ) (a,b,c) (a,b,c) 出现时,第 n n n 个盘子的位置,若它在第一根柱子上,那么方案数为 f a − 1 , c , b f_{a-1,c,b} fa1,c,b;若它在第三根柱子上,那么方案数为 f b , a , c − 1 f_{b,a,c-1} fb,a,c1。从而 f a , b , c = f a − 1 , c , b + f b , a , c − 1 f_{a,b,c}=f_{a-1,c,b}+f_{b,a,c-1} fa,b,c=fa1,c,b+fb,a,c1

    f f f 对应的生成函数为 F ( x , y , z ) F(x,y,z) F(x,y,z)。那么上式意味着 F ( x , y , z ) = x F ( x , z , y ) + z F ( y , x , z ) + 1 F(x,y,z)=xF(x,z,y)+zF(y,x,z)+1 F(x,y,z)=xF(x,z,y)+zF(y,x,z)+1,加一是因为 f 0 , 0 , 0 = 1 f_{0,0,0}=1 f0,0,0=1

    这个生成函数和一般的生成函数不太一样,因为它涉及到下标的位置交换,即维度与维度之间的交换。那么仅靠这一条方程是不够的,我们要把其他对称的方程都写出来。

    F x = F ( y , x , z ) = F ( z , x , y ) F_x=F(y,x,z)=F(z,x,y) Fx=F(y,x,z)=F(z,x,y) F y = F ( x , y , z ) = F ( z , y , x ) F_y=F(x,y,z)=F(z,y,x) Fy=F(x,y,z)=F(z,y,x) F z = F ( x , z , y ) = F ( y , z , x ) F_z=F(x,z,y)=F(y,z,x) Fz=F(x,z,y)=F(y,z,x)。类似地我们可以得出三条等式:
    F y = x F z + z F x + 1 F x = y F z + z F y + 1 F z = x F y + y F x + 1 F_y=xF_z+zF_x+1\\ F_x=yF_z+zF_y+1\\ F_z=xF_y+yF_x+1 Fy=xFz+zFx+1Fx=yFz+zFy+1Fz=xFy+yFx+1

    解得 F ( x , y , z ) = F y = ( 1 + y ) ( 1 + x − y + z ) 1 − x 2 − y 2 − z 2 − 2 x y z = ( 1 + y ) ( 1 + x − y + z ) ∑ i = 0 ∞ ( x 2 + y 2 + z 2 + 2 x y z ) i F(x,y,z)=F_y=\dfrac{(1+y)(1+x-y+z)}{1-x^2-y^2-z^2-2xyz}=(1+y)(1+x-y+z)\sum\limits_{i=0}^{\infty}(x^2+y^2+z^2+2xyz)^i F(x,y,z)=Fy=1x2y2z22xyz(1+y)(1+xy+z)=(1+y)(1+xy+z)i=0(x2+y2+z2+2xyz)i

    ( 1 + y ) ( 1 + x − y + z ) (1+y)(1+x-y+z) (1+y)(1+xy+z) 展开,再枚举 i i i,要算的就是 [ x a y b z c ] ( x 2 + y 2 + z 2 + 2 x y z ) i [x^ay^bz^c](x^2+y^2+z^2+2xyz)^i [xaybzc](x2+y2+z2+2xyz)i。这个东西可以 O ( 1 ) O(1) O(1) 算:考虑 2 x y z 2xyz 2xyz 这一项在乘积中的幂次 k k k,那么需要满足 3 k + 2 ( i − k ) = a + b + c 3k+2(i-k)=a+b+c 3k+2(ik)=a+b+c,容易解出 k k k,然后容易解出 x 2 , y 2 , z 2 x^2,y^2,z^2 x2,y2,z2 分别在乘积中的幂次,然后用组合数一算就好了。

    时间复杂度 O ( 3 popc ⁡ ( n ) × n ) O(3^{\operatorname{popc}(n)}\times n) O(3popc(n)×n)

  • 相关阅读:
    linux系统配置之单一网卡配置多个不同网段IP(centos)
    MySQL使用GROUP BY分组,根据分组值对应,为null的分组展示为0或自定义数值
    C++之打印编译全过程(二百一十四)
    剑指 Offer 12. 矩阵中的路径【中等难度】
    那些在开源世界顶半边天的女同胞们
    Spring中常见的参数解析器
    Qt+C++自建网页浏览器-Chrome blink最新内核基础上搭建-改进版本
    React入门
    DE算法简介
    全局Ceph节点宕机处理
  • 原文地址:https://blog.csdn.net/ez_lcw/article/details/127106511