致我们终将忘记的算法(随处可见的经典<2>)

********Catalan数*********

Catalan数(卡塔兰数)取自组合数学中一个常在各种计数问题出现的数列。卡塔兰数的一般项公式为:

令其为h(n)的话,满足h(n)=h(0)*h(n-1)+h(1)*h(n-2)+......+h(n-1)h(0)  (n>=2).若从中取出的数叫做第n个Catalan数,前几个Catalan数是:

1,1,2,5,14,42,132,429,1430,4862,16796,58786,208012,742900,2674440,9694845,.........这么一看没有什么特别的,但是Catalan数却是许多计数问题的最终形式

卡塔兰数的变形:

Catalan数的程序求解:

long Catalan(int n){

if(n<=1)  return 1;

long *h=new long[n+1]();

h[0]=h[1]=1;

for(int i=2;i<=n;i++){

h[i]=0;

for(int j=0;j<i;j++)  h[i]+=(h[j]*h[i-1-j]);     //根据公式h(i)=h(0)*h(i-1)+h(1)*h(i-2)+......+h(i-1)h(0);

}

long result=h[n];    delete []h;

return result;

}

********Catalan数常见的应用********

1->括号的匹配问题(n对括号有多少种匹配方式?)

解题:n对括号相当于2n个符号,n个左括号、n个右括号,可以设问题的解为f(2n).第0个符号肯定为左括号,与之匹配的右括号必须为2*i+1.因为如果是第2*i个字符,那么第0个字符与第2i个括号之间包含了奇数个括号,无法构成匹配。

通过分析,f(2n)可以转化为如下的递推式f(2n)=f(0)*f(2n-2)+f(2)*f(2n-4)+.....+f(2n-4)*f(2)+f(2n-2)*f(2).该递推式的解释如下:f(0)*f(2n-2)表示第0个字符与第1个匹配。同时剩下的字符分成两部分,一部分为0个字符,另一部分分为2n-2个字符,然后分别对这两部分进行求解。f(2)*f(2n-4)表示第0个字符与第3个字符匹配,剩下部分分成两个一个包含2个括号,一个包含2n-4个括号,依次类推。

假设f(0)=1,计算下面项f(2)=1,f(4)=2,f(6)=5.刚好f(2n)=h(n)

2->进栈出栈问题(一个无穷大的栈,进栈序列为1,2,3,......,n,有多少个不同的出栈序列)

解题:这个与加括号的很相似,进栈操作相当于是左括号,而出栈操作相当于右括号,n个数的进栈次序和出栈次序构成了一个含有2n个数字的序列。第0个数肯定是进栈的数,第2i+1是出栈的数。同上面一题类似可以用Catalan数方法解决

3->二叉树的种类问题(n个结点构成的二叉树,共有多少种情形?)

可以按照如下的方式考虑:根结点肯定会占用一个结点,那么剩余的n-1个结点可以有如下的分配方式,T(0,n-1),T(1,n-2),T(2,n-3).....T(n-1,0),其中T(i,j)表示根的左子树含有结点i,右子树含有结点j

上述问题用递归式表达:f(n)=f(0)*f(n-1)+f(1)*f(n-2)+......+f(n-1)f(0)  刚好和Catalan数的递推公司相同。

4->网格路径问题

对于一个n*n的正方形网格,每次我们能向右或者向上移动一个格,那么从副对角的左下方到右上方的路径(均在副对角线的下方)总数为多少?

对于这样的题目,我们将一条水平线标记为进栈操作,垂直线标记为出栈操作。要保证前进的k步总水平的格子数多于垂直的格子数。转为理解为出栈的时候必须有元素,很明显又是一个Catalan数问题。

5->凸多边形分割问题(求一个凸多边形区域划分成三角形区域的方法数?)

解题:以凸多边形的一边为基,设这条边的2个顶点A和B,从剩余顶点中选一个,可以将凸多边形分成三个部分,中间一个是三角形,左右两边分别为凸多边形,然后再分别求解这两个凸多边形。

设问题的解f(n),其中n表示顶点的个数,那么f(n)=f(2)*f(n-1)+f(3)*f(n-2)+....+f(n-2)*f(3)+f(n-1)*f(2).其中f(2)*f(n-1)表示相邻三个顶点构成一个三角形,那么另外两个部分的顶点数分别为2和n-1个

设f(2)=1,那么f(3)=1,f(4)=2,f(5)=5,依次类推f(n)=h(n-2)

6->集合划分问题(对于集合1,2,3,....,2n的不交叉的划分数目?)

不交叉的划分定义为:对于集合{a,b}和{c,d},假设它们组成的区间[a,b]和[c,d]我们假设两个区间不重合,那么以下4种为不交叉的情况:a<c<d<b  a<b<c<d  c<a<b<d c<d<a<b

对于集合{1,2,3,.....2n}将里面的元素两两分为一个子集,共n个,若任意两个子集都是不交叉的,那么我们称此时的划分为一个不交叉划分。我们可以将每个子集中的较小数用左括号代替,较大数用右括号代替,此时就转换位括号匹配问题,用Catalan求解。

7->矩阵连乘问题

矩阵连乘的括号化方案为f(n),那么问题的解为 f(n)=f(1)*f(n-1)+f(2)*f(n-2)+......+f(n-1)*f(1),转化为catalan数为h(1)

8->连线不相交问题(在园上有2n个点,将这些点成对连接起来使得所得的n条线段不想交的方法数?)

我们做这样的考虑,以其中一个点为基点,编号为0,然后按照顺时针方向将其他点依次编号。那么与0号相连的必定为奇数。否则,这两个编号间含有奇数个点。必定会使得有个点被孤立。若两个点相连之后就被划分为两个部分,一个部分含i个点,另一个部分含j个点。有递推式:f(n)=f(0)*f(n-2)+f(2)*f(n-4)+.....+f(n-4)*f(2)+f(n-2)*f(0).点的个数为2n转化为求h(n)

9->高矮排队问题(2n个高矮不同的人,排成两排,每排必须是从矮到高排列,而且第2排比对应第一排的人高,问有多少种排列方式)

先将2n个人从低到高排列,然后用0表示对应的人在第一排,用1表示对应的人在第2排。那么总共有n个0和n个1。那么对应着一种方案:比如0000.....11111对应着第一排为1到n,第2排位n+1到2n 问题就转化为满足这样的01序列的个数。

观察1的出现,我们考虑它能不能放在第2排,那么在这个1之前出现的那些0和1对应的人,要么在1的左边要么在1的前面。就是要求之前0的个数要大于1.我们可以把0看成入栈1看成出栈,这样问题就转为2n个元素的合法出入栈的顺序。Catalan数问题

10->格子填数问题(在一个2*n的格子中填入1到2n这些数值,使得每个格子内的数值都比右边和上边的大)

和第9题问题类似

11->门票找钱问题(2n个人排成一个行进入剧场,入场费5元,其中只有n个人有一张5元的钞票,n个人只有10员的钞票,问排队的方案)

解题方法:将5元的看成进栈,10员的看成出栈,问题转化为n个元素出栈种类的问题

时间: 2024-10-26 21:37:08

致我们终将忘记的算法(随处可见的经典<2>)的相关文章

《算法竞赛入门经典》动态规划复习

codevs 4979 数塔 1 #define N 100 2 #include<iostream> 3 using namespace std; 4 #include<cstdio> 5 int a[N][N],b[N][N],n; 6 int main() 7 { 8 scanf("%d",&n); 9 for(int i=1;i<=n;++i) 10 for(int j=1;j<=i;++j) 11 { 12 scanf("

《算法竞赛入门经典第二版》 P35 习题2-4 子序列的和(subsequence)

/* <算法竞赛入门经典第二版> P35 习题2-4: 输入两个正整数 n < m < 10^6,输出 (1/n)^2 + 1/(n+1)^2 +……+ 1/m^2,保留5位小数. 输入包含多组数据,结束标志为 m=n=0. 有错欢迎指出^_^ */ #include<stdio.h> int main() { int m,n,i,j=1; while(scanf("%d%d",&m,&n) != EOF) { double sum

算法竞赛入门经典_4.3_递归

看代码 #include <stdio.h> int f(int n){ return n == 0?1:f(n-1)*n; } int main() { printf("%d\n", f(5)); return 0; } 上面f函数使用了递归,递归由两部分组成,一是递归头,二是递归体. 我们使用gcc调试工具 H:\编程书籍学习\算法竞赛入门经典2代码\算法入门经典第四章>b f 'b' 不是内部或外部命令,也不是可运行的程序 或批处理文件. H:\编程书籍学习\算

《算法竞赛入门经典(第二版)》pdf

下载地址:网盘下载 内容简介  · · · · · · <算法竞赛入门经典(第2版)>是一本算法竞赛的入门与提高教材,把C/C++语言.算法和解题有机地结合在一起,淡化理论,注重学习方法和实践技巧.全书内容分为12 章,包括程序设计入门.循环结构程序设计.数组和字符串.函数和递归.C++与STL入门.数据结构基础.暴力求解法.高效算法设计.动态规划初步.数学概念与方法.图论模型与算法.高级专题等内容,覆盖了算法竞赛入门和提高所需的主要知识点,并含有大量例题和习题.书中的代码规范.简洁.易懂,不

算法竞赛入门经典训练指南

最近在看算法竞赛入门经典训练指南这本书,书中不错的算法我将在博客中发布,和大家共同学习. 题目: 在你的王国里有一条n个头的恶龙,你希望雇一些骑士把它杀死(即砍掉所有头).村里有m个骑士可以雇佣,一个能力值为m的骑士可以砍掉一个直径不超过x的头,且需要支付x个金币.如何雇佣骑士才能砍掉恶龙的所有头,且需要支付的金币最少?注意,一个骑士只能砍一个头(且不能被雇佣两次). 输入格式: 输入包含多组数据.每组数据的第一行为正整数m和n(1<=m,n<=20 000):以下m行每行为一个整数,即恶龙每

算法竞赛入门经典-训练指南(10881-Piotr&#39;s Ants)

题目大意: 一根长度为L的木棍一堆蚂蚁爬,向左或向右,速度都为1,若两蚂蚁碰撞则同时转头(转身时间忽略不计),问T时间之后每只蚂蚁的位置: 输入:t,(t个样例),每个样例输入 L,T,n,接下来是n行每行两个数据,一个POS(位置),一个dir(方向): 输出:按输入顺序输出每只蚂蚁的最终位置,若处于碰撞状态则输出Turning,掉下去输出"Fell off": 解题思路: 本题类似于<挑战程序设计>的一道水题(POJ -1852  Ants),思路题:不过本题输入并不一

算法竞赛入门经典+挑战编程+USACO

下面给出的题目共计560道,去掉重复的也有近500题,作为ACMer Training Step1,用1年到1年半年时间完成.打牢基础,厚积薄发.   一.UVaOJ http://uva.onlinejudge.org  西班牙Valladolid大学的程序在线评测系统,是历史最悠久.最著名的OJ.   二.<算法竞赛入门经典> 刘汝佳  (UVaOJ  351道题)  以下部分内容摘自:http://sdkdacm.5d6d.com/thread-6-1-1.html   "AO

棋盘覆盖问题(算法竞赛入门经典)

在一个 2^k * 2^k 个方格组成的棋盘中,若恰有一个方格与其它方格不同,则称该方格为一特殊方格,称该棋盘为一特殊棋盘.显然特殊方格在棋盘上出现的位置有 4^k 种情形.因而对任何 k>=0 ,有 4^k 种不同的特殊棋盘.下图所示的特殊棋盘为 k=2 时 16 个特殊棋盘中的一个. 在棋盘覆盖问题中,要用下图中 4 中不同形态的 L 型骨牌覆盖一个给定的特殊棋牌上除特殊方格以外的所有方格,且任何 2 个 L 型骨牌不得重叠覆盖.易知,在任何一个 2^k * 2^k 的棋盘中,用到的 L 型

【算法竞赛入门经典】【第三章】课后习题(第二部分)

自从蓝桥杯之后,都没写博客了.今天将之前第三章还差的一部分习题答案补上. 3-4整数相加 这一题题目有提示,说选择合适的输入方式,即可简化问题.刚开始没想到cin,结果还用字符串来做,多亏别人提醒我一下,我才想起cin.惭愧啊.. #include <iostream> using namespace std; int main() { int a,b; char op; while(cin>>a>>op>>b){ switch(op){ case '+':