其他分享
首页 > 其他分享> > 紫书学习 10.数学概念与方法--递推和计数问题

紫书学习 10.数学概念与方法--递推和计数问题

作者:互联网

紫书给出了三个非常经典的递推模型。

汉诺塔问题

要求将A杆子的圆盘全部移到B杆子,并保持同样的叠放顺序。

要求:

  1. 每次只能移动顶部的盘子。
  2. 圆盘可以插在任一杆子上。
  3. 任何时刻都不能把小盘放在大盘上面。

求移动需要的步数。

解法:

假设移动n个盘子的方案是\(f(n)\),要把n个盘子移到B柱子,可以先把n-1个较小的盘子移到C柱子,再把最大的盘子移到B柱子,最后再把C柱子上的n-1个盘子移到B上。得出递推式:

\[f(n)=2*f(n-1)+1 \]

基于递推式观察和归纳得到更简洁的写法:

\[f(n)=2^n-1 \]

汉诺塔问题是经典的递归入门题,分析中的“先把n-1个较小的盘子移动到C柱子上”就运用了递归思想。

Fibonacci数列

爬台阶问题

大意:要爬上n级台阶,每次可以爬一级或者两级,问爬上n级台阶有多少种方案。

解答:假设爬上i级台阶有\(f(i)\)种方案数。显然\(f(0)=1\),\(f(1)=1\),\(f(2)=2\)。要爬上三级台阶,可以先走一步到一级再一次爬两级,也可以先爬两级再一次爬一级,所以\(f(3)=f(1)+f(2)\)。进一步归纳得递推关系式:

\[f(i)=f(i-1)+f(i-2) \]

和斐波那契数列递推式一模一样。

兔子问题

大意:现在有雌雄一对新兔子。每对兔子从第二个月开始每月产雌雄一对新兔子。问n个月之后有多少对兔子。

解答:设第i个月有兔子\(f(i)\),可知\(f(1)=f(2)=1\),\(f(3)=2\)。对任意\(f(i)\),第\(i-1\)个月有\(f(i-1)\)对兔子,第\(i-1\)个月的兔子又生了\(f(i-2)\)对兔子,所以有:

\[f(i)=f(i-1)+f(i-2) \]

铺地砖问题

大意:有一两行n列的地面,现有1x22x1两种地砖,问铺满地面有多少种方案。

解答:假设铺满n列的地面有\(f(n)\)种方案,一开始可以先摆一块竖的地砖,剩下部分的铺设方案就是\(f(n-1)\),也可以先摆两块横的地砖,剩下的铺设方案就是 \(f(n-2)\),所以也有:

\[f(n)=f(n-1)+f(n-2) \]

另一种解答:可以假设在第i列摆放了竖的地砖,于是两边的摆放方案分别是\(f(i-1),f(n-i)\),再枚举所有摆了竖砖的列,就有:

\[f(n)=\sum f(i-1)*f(n-i),1\le i\le n \]

但是这种方法是有很多重复的,因为\(f(i-1)\)肯定也会包含摆放了竖砖的方案。

尝试改进这种方法,可以假设在第i列摆放了第一块竖的地砖,这样就除去了冗余了。

那么递推式如何?分析到如果第i列摆放了第一块竖砖,那么前\(i-1\)列就都要摆放横砖,有1种方案,其中\(i-1\)必须是偶数,否则就不存在这种方案;后面列的摆放方案数就是\(f(n-i)\)。

所以可以得到最后的递推式:

\[f(n)=f(n-1)+f(n-3)+....+f(2)+f(0),n为奇数 \]

\[f(n)=f(n-1)+f(n-3)+.....+f(1)+1,n为偶数 \]

n为偶数时,最后加上的1就是没有竖砖的方案。

最后发现这个数列和斐波那契数列的值完全一样。

应用:Fibonacci数列取模的循环节

如果对斐波那契数列取模,只要项数足够,就一定会出现循环节。具体见题目。

Colossal Fibonacci Numbers! - UVA 11582 - Virtual Judge (vjudge.net)

题目大意:假设斐波那契数列的值为\(f(i)\),给定a,b和n,求\(f(a^b)\%n\),其中\(0 \leq a,b <2^{64}\),\(1\le n\le 1000\)。

解答:题目的数据范围非常大,但是n比较小。注意到,如果数列有相邻两项取模后和是以前出现过的数,那么根据Fibonacci递推式:

\[f(i)=f(i-1)+f(i-2) \]

这相邻两项之后的项就会开始循环。

现在的问题有两个:

  1. 循环节有多长,怎么找?

    每一项取余之后的范围是\([0,n)\),因此每相邻两项的所有排列有\(n^2\)种,根据抽屉原理,只要枚举到第\(n^2+1\)项,就一定会出现相同的排列,即出现了循环节。枚举到\(n^2\)的时间是可以接受的。

  2. 一定从首项\(f(0)=f(1)=1\)开始循环吗

    实际上只要证明\((f(i-2)+f(i-1))\%m=f(i)\),在%m的条件下有唯一解,就能证明只要有\(f(i),f(i-1)\)发生重复,就可以推到\(f(i-1),f(i-2)\)发生重复,一直推回去就知道首项重复。

    这里我不太理解其中的奥秘,希望有大佬能证。。。

CODE:

const int N=1005;
typedef unsigned long long ull;
ull a,b;
int n;
int T;
int f[N*N];

ull qm(ull a,ull k,int p){
	ull tmp=1;
	while(k){
		if(k&1)tmp=tmp*a%p;
		k>>=1;
		a=a*a%p;
	}
	return tmp;
}

int main(){
	scanf("%d",&T);
	f[0]=0;f[1]=1;
	while(T--){
		cin>>a>>b>>n;
		if(n==1 || a==0){ cout<<0<<endl; continue;}
		int loop;
		for(int i=2;i<=n*n;i++){//枚举循环节
			f[i]=(f[i-1]+f[i-2])%n;
			if(f[i]==f[1] && f[i-1]==f[0]){ loop=i-1; break;}
		}
		cout<<f[qm(a%loop,b,loop)]<<endl;
	}
	return 0;
}

应用:铺地砖另一种解法的应用

这种通过限定第一个位置来去除枚举重复的方法非常好用,比如这题。

Critical Mass - UVA 580 - Virtual Judge (vjudge.net)

题目大意:有无数个U和L字符,要求把n个字符组成一个字符串,并且至少有3个U放在一起,问有多少种放法。

解答:类似之前的分析方法,可以假设第一次连续的UUU出现在字符串的第\(i,i+1,i+2\)位上,那么第\(i+2\)位后可以随意摆放,方案就是\(2^{n-i-2}\)种;第i位前面必须保证没有连续3个的U,假设方案为\(g\),那么\(g(i-1)=2^{i-1}-f(i-1)\)。

但是这种方法同样有冗余,因为\(g(i-1)\)中一定会有\(i-1\)位出现U的情况,最后就和“第一次连续的UUU出现在字符串”出现在i矛盾,因此还需要把\(i-1\)位定成L,那么左边就是\(g(i-2)\)。

还有一个小细节就是当第一组UUU出现在首位的时候,前面没法放L,需要另外计算:方案数是\(2^{n-3}\)

CODE:

typedef long long ll;
const int N=35;
int n;
ll f[N],g[N];
int p[N];

int main(){
	f[1]=f[2]=0; f[3]=1;
	g[0]=1; g[1]=2; g[2]=4;
	p[0]=1;
	for(int i=1;i<=N-5;i++)p[i]=2*p[i-1];//算出2的幂
	for(int n=4;n<=N-5;n++){
		g[n-1]=p[n-1]-f[n-1];
		f[n]=p[n-3];
		for(int i=2;i<=n-2;i++){
			f[n]+=g[i-2]*p[n-i-2];
		}
	}
	
	while(scanf("%d",&n) && n){
		cout<<f[n]<<endl;
	}
	return 0;
}

Catalan数

卡特兰数常用表达式

涉及对角线的非降路径

非降路径

从\((0,0)\)开始走到\((m,n)\),只能向右或者向上走,有多少种走法?

一共要走\(m+n\)步,然后其中有m步是向右走的,答案就是\(C_{m+n}^{m}\)种方案。

Catalan数和非降路径更多是通过组合数\(C\)联系起来的。

不接触非降路径:

考虑从\((0,0)\)走到\((n,n)\)的,不接触对角线\(x=y\)的非降路径。直接根据对称性,只考虑下半部分,由于不经过\(x=y\),所以原路径和从\((1,0)\)走到\((n,n-1)\)的路径等价,因此所有非降路径的数目有\(C_{2n-2}^{n-1}\)种。对于任意一条接触到了对角线的路径(如下图),都可以把前缀一段包括穿过的部分做关于\(x=y\)的对称,最后变成起点为\((0,1)\),终点为\((n,n-1)\)的路径。因此下半部分不接触\(x=y\)的非降路径数目就是\(C_{2n-2}^{n-1}-C_{2n-2}^{n}\)

不穿过非降路径:

类似地可以对穿过对角线的路径进行对称转化,从而求出其数量。每条穿过对角线路径都一定会经过下图中的黑色虚对角线,我们把后缀一段包括穿过\(y=x\)对角线的部分关于黑色虚线做对称,最后注意到穿过对角线的路径就等价于从 \((0,0)\)走到\((n-1,n+1)\)的路径。因此下半部分不穿过非降路径的数目是\(C_{2n}^{n}-C_{2n}^{n-1}=\frac{C_{2n}^{n}}{n+1}\)

非降路径的应用

以下计数问题都可以转化成非降路径问题:

  1. 有\(2n\)个人排成一行进入剧场。入场费 5 元。其中只有\(n\)个人有一张 5 元钞票,另外\(n\)人只有 10 元钞票,剧院无其它钞票,问有多少种方法使得只要有 10 元的人买票,售票处就有 5 元的钞票找零?

    解法:建立坐标轴,横轴上+1表示花5元,纵轴上+1表示花10元,最后一种合法方案就是从\((0,0)\)到\((n,n)\)的一条不穿过非降路径。

  2. 一个栈(无穷大)的进栈序列为\(1,2,3,4,5,...,n\),有多少个不同的出栈序列?

    解法:每次可以考虑出栈 或 进栈,但是到目 前为止进栈的总次数必须大于等于出栈的总次数。建立坐标系,把进栈看成横轴+1,出栈看成纵轴+1,出进栈的次数总共是\(2n\),所以就转化成了不穿过非降路径问题。

划分

以下划分问题更贴近的是Catalan的递推形式

  1. 对角线不相交的情况下,将一个凸\(n\)边形区域分成三角形区域的方法数?

    对每个顶点从1到n编号,以边\((1,n)\)为三角形的一边,枚举三角形的另一个点,假设三角形的另一个点在\(k\)位置,于是n边形就被分成了\(k\)边形和\(n-k+1\)边形,于是可以写出式子:

    \[f(n)=f(2)f(n-1)+f(3)f(n-2).....+f(n-1)(2) \]

  2. 在圆上选择\(2n\)个点,将这些点成对连接起来使得所得到的\(n\)条线段不相交的方法数?

    可以类似1的作划分,从1点开始,枚举连接线段\((1,k)\)

  3. \(n\)个结点可构造多少个不同的二叉树?

    可以类似中序遍历的思想,枚举根节点,划分出左子树和右子树。

标签:10,紫书,方案,--,路径,int,非降,2n,递推
来源: https://www.cnblogs.com/tshaaa/p/16505138.html