一道算法题的思考-管道取珠

最近做了一道算法题,觉得很有意思。记录如下:

题目大意

管道取珠

 

有一个游戏,左侧有两个上下两个管道,右侧有一个输出管道。游戏初始时,左侧上下两个管道分别有一定数量的小球(有深色球和浅色球两种类型),而右侧输出管道为空。每一次操作,可以从左侧选择一个管道,并将该管道中最右侧的球推入右边输出管道。

假设上管道中有n个球,下管道中有m个球,则整个游戏过程需要进行n+m次操作,即将所有左侧管道中的球移入输出管道。最终n +m个球在输出管道中从右到左形成输出序列

爱好数学的小X知道,他共有C(n+m,n)种不同的操作方式,而不同的操作方式可能导致相同的输出序列。举个例子

我们用A表示浅色球,B表示深色球。并设移动上管道右侧球的操作为U,移动下管道右侧球的操作为D,则共有C(2+1,1)=3种不同的操作方式,分别为UUD, UDU, DUU;最终在输出管道中形成的输出序列(从右到左)分别为BABBBABBA。可以发现后两种操作方式将得到同样的输出序列

假设最终可能产生的不同种类的输出序列共有K种,其中第i种输出序列的产生方式(即不同的操作方式数目)ai个。聪明的小X早已知道,

因此,小X希望计算得到

你能帮助他计算这个值么?由于这个值可能很大,因此只需要输出该值对1024523的取模即可(即除以1024523的余数)

说明:文中C(n+m, n)表示组合数。组合数C(a,b)等价于在a个不同的物品中选取b个的选取方案数。

题解:

方法一

按照问题的要求,最容易想到的就是求出每种不同序列的取数方法,最后求出平方和。

  • 具体如何求出每种序列的取法数?由于只有两种珠子,可以使用二进制01表示每种珠子。具体可以构建一个数组或者任何一种线性存储结构,其下标对应每种序列的二进制表示,数组的值对应取数方法。采用递归遍历每种取珠方法。算法复杂度为O(2^(m+n))

方法二

利用将乘法转化加法的思想对问题做转换。

定义变量A表示取出所有m+n个珠子的一种取珠方法

定义变量B表示取出所有m+n个珠子的一种取珠方法

定义(A)表示通过A方法取得序列结果

定义(B)表示通过B方法取得的序列结果

T(A,B) 表示所有满足(A)==(B)(A,B)序列对的个数。其中A B互相独立。

可以看得出T(A,B)即要求的结果。

这种表示方法本质上是将乘法转化为加法,因为假如某个序列结果X其有n种取珠方法 X1X2…..Xn那么其对应的(A,B)序列对就有个。那所有的序列结果其对应的(A,B)序列对个数之和就是待求结果。

 

问题是如何求T(A,B)

问题的关键就是把所有的满足(A)==(B)(A,B)序列对个数求出来,又要判断两个序列结果是否相同,又要计算生成序列方法数,那么这个序列对个数一定要和取珠得过程相关联,而一个取珠的过程的某个状态可以表示为上面的管道取了a个,下面的管道取了b个。因此可以用[a,b]表示一个取球的状态,[m,n]即表示已经取完所有球,将其扩展位一个序列对(a,b,c,d)表示一个二元组的取球序列状态,F[a,b,c,d]{其中a+b=c+d}表示所有满足(a+b)==(c+d)(a+b,c+d)序列对个数。那么F[m,n,m,n]即为所求的结果。F[0,0,0,0]=0

F[i,j,k,l]递推方程

F[i+1,j,k+1,l] = F[i+1,j,k+1,l] +F[i,j,k,l]   (上管道i+1个球等于上管道k+1个球)

F[i,j+1,k+1,l] = F[i,j+1,k+1,l] +F[i,j,k,l]   (下管道j+1个球等于上管道k+1个球)

F[i+1,j,k,l+1] = F[i+1,j,k,l+1] +F[i,j,k,l]   (上管道i+1个球等于下管道l+1个球)

F[i,j+1,k,l+1] = F[i,j+1,k,l+1] +F[i,j,k,l]   (下管道j+1个球等于下管道l+1个球)

状态个数,因此复杂度为。因为需要满足(a+b)==(c+d)因此又可以减少一维,复杂度为O(m^2*n)

很多人将该问题归结为DP题,但是我认为该问题于DP关系不大,他并不涉及重复子问题,以及状态无后效性。只是一个巧妙的递推计数。

该算法能够将算法复杂度由O(2^(m+n))降至O(m^2*n),关键在于将乘法转化为加法,从而避开了计算每种取珠结果对应的取法数这个问题。

 

 

 

 


 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值