HDU OJ -- Queuing(ACM Steps: 3.1.8)

一、概述

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;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值