能力强的人善于解决问题,有智慧的人善于绕过问题。 区别很微妙,小心谨慎做后者。
全部博文(399)
分类: LINUX
2009-10-31 17:32:50
一楼:
*
* *
* * *
* * * *
* * * * *
形如这样的直角三角形网格,从左上角开始,只能向右走和向下走,问总共有多少种走法?
问题的由来:编号为 1 到 n 的 n 个元素,顺序的进入一个栈,则可能的出栈序列有多少种?
对问题的转化与思考:n 个元素进栈和出栈,总共要经历 n 次进栈和 n 次出栈。这就相当于对这 2n 步操作进行排列。
一个模型:一个 n*n
的正方形网格,从左上角顶点到右下角顶点,只能向右走和向下走。问共有多少种走法。如果将向右走对应上述问题的进栈,向下走对应上述问题的出栈,那么,可
以视此模型为对上述问题的具体描述。而解决此问题,只要在总共从左上角到右下角的2n步中,选定向右走的步数,即共有C(n 2n)中走法。
但是存在一个问题,如果走法越过了对角线,那么对应到上述问题是出栈数比入栈数多,这是不符合实际的。
对以上模型进行处理,对角线将以上正方形网格分成两部分,只留下包含对角线在内的下半部分,那么就不会出现越过对角线的问题。而这问题就是开始提出的问题。走法总数不等于C(n 2n)/2 ,因为C(n 2n)种走法中,有的越过了边界。
问题等价于:n个1和n个0组成一2n位的2进制数,要求从左到右扫描,1的累计数不小于0的累计数,试求满足这条件的数有多少?
解答: 设P2n为这样所得的数的个数。在2n位上填入n个1的方案数为 C(n 2n)不填1的其余n位自动填以数0。从C(n
2n)中减去不符合要求的方案数即为所求。不合要求的数指的是从左而右扫描,出现0的累计数超过1的累计数的数。不合要求的数的特征是从左而右扫描时,必
然在某一奇数2m+1位上首先出现m+1个0的累计数,和m个1的累计数。此后的2(n-m)-1位上有n-m个1,n-m-1个0。如若把后面这部分
2(n-m)-1位,0与1交换,使之成为n-m个0,n-m-1个1,结果得1个由n+1个0和n-1个1组成的2n位数,即一个不合要求的数对应于一
个由n-1个0和n+1个1组成的一个排列。
反过来,任何一个由n+1个0,n-1个1组成的2n位数,由于0的个数多2个,2n是偶数,故必在某一个奇数位上出现0的累计数超过1的累计数。同样在
后面的部分,令0和1互换,使之成为由n个0和n个1组成的2n位数。即n+1个0和n-1个1组成的2n位数,必对应于一个不合要求的数。
用上述方法建立了由n+1个0和n-1个1组成的2n位数,与由n个0和n个1组成的2n位数中从左向右扫描出现0的累计数超过1的累计数的数一一对应。
例如是由4个0和4个1组成的8位2进制数。但从左而右扫描在第5位(显示为红色)出现0的累计数3超过1的累计数2,它对应于由3个1,5个0组成的10100010。
反过来对应于 10100101因而不合要求的2n位数与n+1个0,n-1个1组成的排列一一对应,故有P2n = C(n 2n)― C(n+1
2n)这个结果是一个“卡塔兰数”Catalan,在组合数学中有介绍,可以参阅有关资料。若有问题可以大家一起交流啊!还有种方法是用我提出的那个模型
的,但是那解法还没怎么弄明白,待我明白了到时候再和大家探讨!
二楼:
楼主的解法很妙,我想是否可以用一种更合乎思维习惯的方式解决这个问题呢。
可以把这个问题描述为一个二元组,(n, 0) 表示有n个元素等待进栈, 0 个元素已进栈, 这相当于问题最初的状况.
接着问题转化为(n-1,1). 可以这么说(n,0) = (n-1,1). 而对于(n-1,1)则相当于(n-1,0)+(n-2,2).
(n-1,0)表示栈中的一个元素出栈, (n-2, 2)表示又有一个元素入栈.
把问题一般话,则(n,m)的排列问题可以转化为(n,m-1)+(n-1,m+1) 此时m>=1,
因为必须栈中有元素才可以出栈.当m=0则(n,0)的问题只能转化为(n-1,1). 当问题为(0,
m)时得到递归边界,这个问题的解是只有一种排列.
我写了程序如下:
#include
#define ELEMNUM 6;
int getPermuStack(int n, int m)
{
if(n == 0)//递归边界
return 1;
if(m == 0)//(n,0)问题的处理
return getPermuStack(n-1, 1);
return getPermuStack(n, m-1) + getPermuStack(n-1, m+1);
}
int main()
{
printf("The total count of stackout permutation is %d.", getPermuStack(6, 0));
return 0;
}
运行结果:The total count of stackout permutation is 132.和楼主得到的算式计算的结果相同.
三楼:
这种方法是可行的,但在实际编程中最好不要用递归,这样如果递归次数一多就容易造成栈溢出。你可以试下用较大的参数来调用你的函数,会造成runtime error的。而且纯粹的递归会造成大量的重复计算:比如,你在计算getPermuStack(5, 5)的时候计算了getPermuStack(5, 4),然后在计算getPermuStack(5,3)的时候又计算了一遍。 当然可以通过动态规划的思想设置一个二维数组来记录计算结果,可是太消耗空间。所以说如果直观的方法就能很高效地解决问题的话,就不会有那么多人去从数学 上求解了。呵呵。
四楼: