一、概述
1. 问题描述
在一个长度为L的队列中,有男人也有女人,m为男人,f为女人,若在队列中出现“fmf”或者“fff”的组合,将此队列定义为O-queue,否则为E-queue。求:(长度为L的队列中E-queue的数目)对(M)取模的结果。
(0 <= L <= 10^6,1 <= M <= 30)
2. 问题链接
HDU OJ -- Queuing(ACM Steps: 3.1.8)
3. 问题截图
图1.1 问题截图
二、算法思路
由于O-queue出现的情况需要队列L至少长度为3,所以当L<3时,队列都是E-queue类型,即f(L)为队列为L时E-queue的数目,则有:
f(1) = 2
f(2)= 2 x 2 = 4
当L = 3时,可能的情况如图2.1所示。
图2.1 n=3时队列的情况
图中红色虚线表示O-queue,此时有f(3)= 6。
当L = 4时,可能的情况如图2.2所示。
图2.2 n=4时队列的情况
可以发现,是以L = 3为基础进行构成的,因为f(3)的结果表示L = 3时,不包含“fmf”或“fff”的队列个数,由于题目规定,“fmf”与“fff”不能出现在队列中,即不能出现在之前的任何位置,则L = 4时, 肯定要在L = 3的基础上找到满足条件的E-queue。
从L = 3到L = 4,如何找到新的满足条件的队列呢?
由于从L = 3到L = 4,要在L = 3的队列基础上增加1个人,并且需要满足增加了这个人之后队伍中任意3个人不出现“fmf”或“fff”组合,由于前3个人是L = 3的结果已经满足,所以此时只要分析最后2个人和新增加的人的组合情况,在L = 3时,最后2个人的性别组合以及对应的数目,记为:
mm = 2
mf = 1
fm = 2
ff = 1
各性别组合在新增加一人后,可能的情况如下所示:
mm -> {mmm,mmf}
mf -> {mfm,mff}
fm -> {fmm,fmf}
ff -> {ffm,fff}
因为最后的结果是统计满足条件的叶节点的数目作为队列长度为L时的结果,并且根据上述,只有mm或mf组合会在原有的分支中派生出新的叶节点,所以f(4) = f(3)+ mm[3] + mf[3],在这里mm[3]是L = 3时mm组合的数目。
可以得出更加一般的关系f(n)= f(n-1)+ mm[n-1] + mf[n-1]
所以问题的关键在于统计出各规模mm、mf组合数。
由于各种组合派生出的分支是固定的,所以对于L = 4时最后两个的性别组合,相对于L = 3时,有这样的关系:
mm[4] = mm[3] + fm[3],因为L = 3时,每个mm组合会生成mmm,每个fm会生成fmm
mf[4] = mm[3]
fm[4] = mf[3] + ff[3]
ff[4] = mf[3]
同理,可以推导出更一般的关系,这里不再赘述。
到此,可以有这样的解决方案:计算并保存出所有L的结果,再根据M做取模运算,但是通过测试,首先由于结果很大,最后的结果需要使用字符串存储,并且当L很大时会超过题目的限制,其次,对于规模很大的字符串进行运算也会超过题目要求的执行时间,所以这种方案不行。
考虑到题目的结果需要对M取模,并且M是一个不大于30的数,所以对于任意的L,最后的结果范围是(0~29)。
这里可以考虑:直接保存队列长度为L时的结果对于可能的M的取模值,但是有一个问题需要留意。
即,存储的结果,f(n)%M,和f(n-1)%M也有对应的关系吗?有的话,关系是什么?
刚才总结出f(n)= f(n-1)+ x,(x = mm[n-1] + mf[n-1])
那么,f(n)%M和f(n-1)%M有什么关系呢?
这里有一个关于取模的理论,在后面会进行证明:
若a%b = p,c%b = q
则(a + c)% b = (a%b + c%b)% b = (p + q) % b
通俗的说,就是,若a对b取模的结果p,则a+c对b取模的结果为p+c对b取模的结果
根据理论,f(n)%M和f(n-1)%M是具有关系的,
将f(n-1)看做a
x,即mm[n-1]、mf[n-1],看做c
f(n)= f(n-1)+ x = a + c
则,f(n)%M,即(a+c)% M = (a%M + c%M)% M,在这里a%M是已经求出的结果,即小规模的L对M取模对应的结果。
综上,在实现时,可以采用二维数组保存结果,一个维度保存不同的M,一个维度保存可能的L值。
理论证明:
若a%b = p,c%b = q
则(a + c)% b = (p + q))% b
证明:
设a = x + y,其中定义x为可以被b整除的部分,即x/b=n,n为整数;定义y为无法被b整除的部分,则a%b = y = p
同理,设c = m + n,c%b = n = q
则(a + c)% b = (x + y + m + n)% b
由于x、m可以被b整除,则x+m也可以被b整除
则有(a + c)% b = (y + n)% b =(p + q))% b
证毕。
三、算法实现
#include <iostream>
using std::cin;
using std::cout;
using std::endl;
const int MAXSIZE = 1000000;
unsigned char ans[29][MAXSIZE];
void calculate()
{
unsigned char mm, mf, fm, ff, tfm, ans0, ans1, ans2, tmp;
ans0 = 2;
ans1 = 4;
ans2 = 6;
for(int i=0; i<29; i++){
tmp = i+2;
ans[i][0] = ans0 % tmp;
ans[i][1] = ans1 % tmp;
ans[i][2] = ans2 % tmp;
mm = 2;
mf = 1;
fm = 2;
ff = 1;
for(int j=3; j<MAXSIZE; j++){
ans[i][j] = (ans[i][j-1]+mm+mf) % tmp;
tfm = fm;
fm = (mf+ff) % tmp;
ff = mf % tmp;
mf = mm % tmp;
mm = (mm+tfm) % tmp;
}
}
}
int main()
{
int L, M;
unsigned long long tmp;
calculate();
while(cin >> L >> M){
if(M == 1)
cout << 0 << endl;
else
cout << (int)ans[M-2][L-1] << endl;
}
return 0;
}