关于几个数

来源:互联网 发布:h5万能表单系统源码 编辑:程序博客网 时间:2024/05/17 09:35

说这些数和公式之前,我想先说下一个非常重要的问题,也就是子问题,所谓子问题,无非就是问题实质和原问题一样,而规模小了一点而已,可能小了1,也可能小了一半,解决一个问题往往要解决它的子问题,然后通过子问题的值推到出原问题的答案,这也是动态规划的精髓之一。

1错排:

n个人编号1n,本来都站在各自的位置上,现在让他们站一排,每个人不能站在原来的位置上去,问有都少种排列方法?

现在我觉得我高中数学老师真水,做题碰到这种问题让我们列一遍。

我之所以前面说了子问题,是因为这里要用到,我假设f(n)表示n个人错排的方法数,那么f(n-1)就是n-1个人的错排方法数,那么假如n-1个人错排好了,我加一个人进去,显然我只能放在前n-1个位置的某个位置,假设我放在了位置k1<=k<=n-1,那么k这个位置的人放哪里呢,他可以放在n,也可以不放在n,我们按照这样两种情况分,第一种,放在n,那么剩下n-2个人,只要他们也错排就好了,即f(n)的一部分值是由(n-1)*f(n-2)来的。第二种,不放在位置n,那么这种情况下还有多少种情况呢,其实我们在回过头考虑下,f(n)这函数的意义,就是说每个数不能放在原来的位置,也就是说,每个数都有一个位置是不能放的,这其实是等价的,那么对于刚才第二种情况,k不能放位置n,其余n-2个数不能放原来的位置,这就是f(n-1)!因此得到公式

f(n)=(n-1)*(f(n-1)+f(n-2))

至于初值,不说也罢,自己算下就好了。

我想说的一点是,这个公式牛逼的地方就是为什么想到按照放不放到n这个位置分开来。

 

2卡特兰数

给你n个左括号和n个右括号,要求把他们排成一排,要保证每个左括号都有一个在它右边右括号和它匹配,问你有多少种排列方法:

我感觉这里有两种意思,算是意味着有两种解题思路吧

首先,我们设f(n)2*n括号的排列方法数,那么第一个括号肯定是左括号,它可以和某个右括号匹配,但是非常重要的一点是假如第一个和第K个括号匹配(2<=k<=2*n),那么k必须是偶数,否则,第一个到第k个直接之间(不包括1k)存在了奇数个符号,肯定满足不了匹配,那么根据k的取值切开后,我们会发现2k-1也是合法的字符串,k+12*n之间也是合法的,才能保证总体是合法的,枚举k后有:

f(n)=f(0)*f(n-1)+f(1)*f(n-2)+.....+f(n-2)*f(1)+f(n-1)*f(0),其中f(0)=1

那么我们是可以在O(n*n)时间内可以把f(n)求出来的。

不过其实这个问题还有另外一层意思 ,那就是对于任意前k个长度的括号串,必须保证左括号的个数大于等于右括号个数。否则右括号比左括号多,那么之后的n-k的括号,左括号比右括号多,是无论如何都满足不了任意一个左括号能和一个右括号匹配的。按照这种思路有一种做法是用所有的排列数,减去非法的字符排列数,不过比较复杂,这里略过。

阿里曾有一道笔试题,给你2*n个人,让他们站成2排,每排n人,必须保证,左边的人比后面的人矮,前面的人比后面的人矮,问你有多少种站法,本题的答案据说是卡特兰数,解释起来比较复杂, 大家可以好好思考下。

 

3斯特林数:

斯特林数有2类:首先说一下第二类斯特林数,给你n物品和一个数k,求把这n个物品分成k个非空集合的方法数。

我觉得这整个问题真的扯到了动态规划,设dp[i][j]表示i个物品分成j个非空集合的方法数,那么整个问题可以由2种状态转移过来,第一,就是第i个物品自己为一类,那么问题转化成了dp[i-1][j-1],第二,第i个物品是加到j类中的某一类的,那么由dp[i-][j]*j,所以有方程:

dp[i][j]=dp[i-1][j-1]+j*dp[i-1][j]

注意几个初值就能解决整个问题了。

第二类斯特林数和第一类的区别是,第二类分完类算是完成任务了,但是第一类还有一个轮换过程,就是它考虑了顺序。第一类斯特林数是指n个物品分成k个非空集合的方法数(集合中有相同的物品但是不同的轮换算不同分法)。

这里说一下,轮换,比如好4个物品围成一个圈,ABCD BCDACDAB,DABC是相同的轮换,其他的4个物品的排列则都不是。所以第一类斯特林数是大于等于第二类,因为第一类在相同的物品下,还有一个排列顺序,而第二类并不考虑顺序。接下来来看方程。

和第一类类似的是,dp[i][j]也有一个状态是dp[i-1][j-1],此时第i个物品自己为一类,所以没有顺序而言,但是对于dp[i-1][j]就是说,我们将第i个物品放到j个类中去,就算是同一类根据不同的位置也算不同的方法,到底有多少种呢。答案是i-1(这个i-1可能不是那么好想),因为插入到物品中间都是不同的情况,所以有方程

dp[i][j]=dp[i-1][j-1]+(i-1)*dp[i-1][j]

关于这种排列组合题,个人觉得多思考练习下逻辑思维能力还是不错的,我觉得动态规划真的是个很神奇的东西,很多问题,都能用它解决

 

0 0
原创粉丝点击