/*
买票找零:
每张球票50元,现有2*n个人排队购票,其中有n个人手持50元的钞票,另外n个人手持100元的钞票,假设开始售票时没有零钱找。问这2*n个人有多少种排队方式,
不至于售票处出现找不开钱的局面?
注意至少要保证排在前面的持50元的人数>=持100元的人数
解法1:
从队首往后数,手持100元的球迷总比手持50元的球迷少,可以把钱召开。
联想到括号匹配问题。假设每个手持50元的球迷是一个左括号"(",而手持100元的球迷是一个右括号")",任意一个售出100元的球迷找回的50元都来自于之前一个
手持50元的球迷
用栈,遍历n个左括号和n个右括号,如果是左括号,则压栈中,如果是右括号,那么让栈顶的左括号出栈,如果始终能够保持栈中有足够的左括号,那么是一个合法
排列。
第0个符号一定是左括号,假设第0个左括号与第k个符号匹配,那么从第一个符号到第(k-1)个符号,第(k+1)个符号到第2n-1个符号也都是一个合法的括号序列。
而k肯定是奇数,否则第1个符号到第(k-1)个符号之间只有奇数个符号就不合法,设k = 2*i+1 (i = 0,1,...,n-1)
0 1 .... 2i 2i+1 2i+2 ... 2n-1
________ _____________
2i个 2n-2i-2个
假设2n个符号中合法的括号序列个数为f(2n),若第0个括号(左括号)与第k=2i+1(i = 01,2,...,n-1)个括号(右括号)匹配,那么剩余括号的合法序列为:
f(2*i) * f(2*n-2*i-2)
得到递推式:
f(2n) = i从0到n-1 对f(2i)*f(2n-2i-2)累加排列数
= [f(0)*f(2n-2)] + [f(2)*f(2n-4)] + [f(4)*f(2n-6)] + ... + [f(2n-2)*f(0)]
其中f(0) = 1,可以用O(n*m)的时间求出问题答案。f(0) = f(2*0) = 0个符号中合法的括号序列个数为1 ? 第0个符号一定是左括号,由于中间没有间隔元素,
第0个符号与第一个符号一定匹配,所以合法括号序列个数为1
解法2:
用1表示50元的球迷,0表示100元的球迷,那么2n个球迷的排队就对应n个1和n个0的排列,例如:1,1,0,0,0,...,1。如果序列的任意前k(k=1,2,...,2n-1)项中1的个数
都不少于0的个数,称这样的序列是合法的,否则称为非法的。合法的序列正好可与行的排列方式一一对应,同时,称由n-1个1和n+1个0组成的序列为Sigma序列
这样的序列总共有个[2*n]个。
[n-1]
n个1和n个0的总排列数位[2*n](2n个位置,选择n个位置给1,剩下的n个给0),即合法序列数和非法序列数的总和。
[n ]
需要求和合法的序列数,先求解非法序列。
在一个非法序列中,存在某个k,使得序列前k项中1的个数小于0的个数。存在某个k,使得序列前k项中1的个数比0的个数刚好少一个,取得其中最小的k。那么这个
序列的后2n-k项中1的个数刚好比0的个数多1,然后2n-k项的0换为1,1换为0,得到一个新序列,有n-1个1和n+1个0组成的Sigma序列。
那么一个Sigma序列能唯一地对应到一个非法序列么?对任意一个Sigma序列,存在某个k,使得序列的前k项中1的个数少于0的个数(因为只有n-1个1,而n+1个0)。
存在某个k,使得前k项中1的个数比0的个数刚好少1个。取其中最小的k,将这个序列中2n-k项中的0变1,1变0,得到一个新序列:n个1和n个0.这个序列正是一个非法
序列。因此Sigma序列对应唯一一个非法序列。
非法序列个数为 {(n-1) {n _ {n-1 = 1/(n+1)* {n
C{2n ,合法序列数等于总个数减去非法序列个数 = C{2n C{2n C{2n
输入:
5(5人手持50,5人手持100)
输出:
42
*/
/*
关键:
1 return (int) (lMolecule/((n+1) * lDemonitator));//合法序列的总个数 = 1/(n+1) Cn 2n
2 n个1和n个0的总排列数位[2*n](2n个位置,选择n个位置给1,剩下的n个给0),即合法序列数和非法序列数的总和。
[n ]
3 在一个非法序列中,存在某个k,使得序列前k项中1的个数小于0的个数。存在某个k,使得序列前k项中1的个数比0的个数刚好少一个,取得其中最小的k。那么这个
序列的后2n-k项中1的个数刚好比0的个数多1,然后2n-k项的0换为1,1换为0,得到一个新序列,有n-1个1和n+1个0组成的Sigma序列。
4
非法序列个数为 {(n-1) {n _ {n-1 = 1/(n+1)* {n
C{2n ,合法序列数等于总个数减去非法序列个数 = C{2n C{2n C{2n
*/
#include <stdio.h>
int validPermutation(int n)
{
long long lDemonitator = 1;
for(int i = 1 ; i <= n; i++)
{
lDemonitator *= i;
}
long long lMolecule = 1;
for(int i = 2*n ; i > n ; i--)
{
lMolecule *= i;
}
return (int) (lMolecule/((n+1) * lDemonitator));//合法序列的总个数 = 1/(n+1) Cn 2n
}
void process()
{
int n;
while(EOF != scanf("%d",&n))
{
printf("%d\n",validPermutation(n));
}
}
int main(int argc,char* argv[])
{
process();
getchar();
return 0;
}
编程之美:第四章 数字之趣 4.3买票找零
最新推荐文章于 2016-10-15 12:51:20 发布