紫书学习 10.数学概念与方法--递推和计数问题
作者:互联网
紫书给出了三个非常经典的递推模型。
汉诺塔问题
要求将A杆子的圆盘全部移到B杆子,并保持同样的叠放顺序。
要求:
- 每次只能移动顶部的盘子。
- 圆盘可以插在任一杆子上。
- 任何时刻都不能把小盘放在大盘上面。
求移动需要的步数。
解法:
假设移动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列的地面,现有1x2
和2x1
两种地砖,问铺满地面有多少种方案。
解答:假设铺满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) \]这相邻两项之后的项就会开始循环。
现在的问题有两个:
-
循环节有多长,怎么找?
每一项取余之后的范围是\([0,n)\),因此每相邻两项的所有排列有\(n^2\)种,根据抽屉原理,只要枚举到第\(n^2+1\)项,就一定会出现相同的排列,即出现了循环节。枚举到\(n^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}\)
非降路径的应用
以下计数问题都可以转化成非降路径问题:
-
有\(2n\)个人排成一行进入剧场。入场费 5 元。其中只有\(n\)个人有一张 5 元钞票,另外\(n\)人只有 10 元钞票,剧院无其它钞票,问有多少种方法使得只要有 10 元的人买票,售票处就有 5 元的钞票找零?
解法:建立坐标轴,横轴上+1表示花5元,纵轴上+1表示花10元,最后一种合法方案就是从\((0,0)\)到\((n,n)\)的一条不穿过非降路径。
-
一个栈(无穷大)的进栈序列为\(1,2,3,4,5,...,n\),有多少个不同的出栈序列?
解法:每次可以考虑出栈 或 进栈,但是到目 前为止进栈的总次数必须大于等于出栈的总次数。建立坐标系,把进栈看成横轴+1,出栈看成纵轴+1,出进栈的次数总共是\(2n\),所以就转化成了不穿过非降路径问题。
划分
以下划分问题更贴近的是Catalan的递推形式。
-
对角线不相交的情况下,将一个凸\(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) \] -
在圆上选择\(2n\)个点,将这些点成对连接起来使得所得到的\(n\)条线段不相交的方法数?
可以类似1的作划分,从1点开始,枚举连接线段\((1,k)\)
-
\(n\)个结点可构造多少个不同的二叉树?
可以类似中序遍历的思想,枚举根节点,划分出左子树和右子树。
标签:10,紫书,方案,--,路径,int,非降,2n,递推 来源: https://www.cnblogs.com/tshaaa/p/16505138.html