两种特殊的排序组合计数公式

                  /* 两种特殊的排序组合计数公式*/
/*
  第一种:Calalan数
  令h(1)=1,h(2)=1;
  h(n)=h(1)h(n-1)+h(2)h(n-2)+...+h(n-1)h(1);
  另类递推式:
  h(n)=h(n-1)*(4*(n-1)-2)/((n-1)+1);
  递推关系解:
  h(n+1)=c(2n,n)/(n+1)(n=1,2,3...);
  递推关系的另类解为:
  h(n+1)=c(2n,n)-c(2n,n+1)(n=1,2,3...);
  注:c(n,r)是组合数
  例如:
  1.一个栈(无穷大)的进栈序列为1,2,3...,n,有h(n+1)个不同的出栈序列;
  2.凸多边形的三角规划分:在一个凸多边形中,通过若干个条互不相交的对角线,把这个多边形划分了若干个三角形。
                        凸n边形的不同划分方案数为h(n-1)(n=2,3,4...);
  3.给定节点组成二叉树:给定N个节点,能够成h(n)种不同二叉树。
  4.矩阵链接:p=a1*a2*a3*...*an,依据乘法结合律,不改变其顺序,只用括号表示成对的乘积,
              有h(n)种括号方案
*/
/*
  5.poj 2084
  这是一个很小但是很古老的游戏,请以顺时针在地上写下连续的数,1,2,3,4,5,6...2n-1,n,(n<=100)
  形成一个圆圈:然后,画一些直线线段,将这些整数连成对,每一个数都必须连接到另一个数。
  而且没有两条线段是相交。当你写下来2n个数后,你能告诉大家可以用多少不同的方式将这些数连接成对。
  输入每行一个正整数n,最后一个-1,输出:2n个连成对会有多少种连法
  分析:
  设这条线段左边有i对点,则其右边有n-i-1对点,这样我们可得递推公式,f(i)=f(1)*f(i)+f(2)*f(i)+...+f(i)f(1);
  可得;f(i)=h(i+1),其中h为catalan数
  离线计算Catalan序列,由于超过上线,采用高精度。
*/
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
struct BigNum//高精度
{
	int s[200];//整数数组
	int leng;//整数位数
}c[120];
BigNum operator*(BigNum a,int b)
{
	int i;
	for(i=0;i<a.leng;i++) a.s[i]*=b;
	for(i=0;i<a.leng;i++)
	{
		a.s[i+1]+=a.s[i]/10;
		a.s[i]%=10;
	}
	while(a.s[a.leng]!=0)
	{
		a.s[a.leng+1]+=a.s[a.leng]/10;
		a.s[a.leng]%=10;
		a.leng++;
	}
	return a;
}
BigNum operator/(BigNum a,int b)
{
	int i;
	for(i=a.leng-1;i>0;i--)
	{
		a.s[i-1]+=(a.s[i]%b)*10;
		a.s[i]/=b;
	}
	a.s[0]/=b;
	while(a.s[a.leng-1]==0) a.leng--;
	return a;
}
void print(BigNum a)
{
	int i;
	for(i=a.leng-1;i>=0;i--)
	{
		printf("%d",a.s[i]);
	}
	printf("\n");
}
int main()
{
	int n;
	c[0].leng=1;c[0].s[0]=1;
	int i;
	for(i=0;i<=101;i++)
	{
		c[i+1]=(c[i]*(4*i+2))/(i+2);//根据递推公式h(n)=h(n-1)*(4*n-2)/(n+1)离线计算Catalan序列
	}
	while(~scanf("%d",&n)){
		if(n<0) break;
		print(c[n]);
	}
	return 0;
}


/*
第二种   Bell数和Stiring数

  Bell数又称为贝尔数。B(n)是包含n个元素的集合的划分方法的数目。
  例如:B(0)=1;B(1)=1;B(2)=2;B(3)=5;B(4)=15;B(5)=52;B(6)=203;
  递推公式:B(0)=1;B(n+1)=c(n,0)B(0)+c(n,1)B(1)+...+c(n,n)B(n);

  Stiring数又称为斯特灵数。在组合数学中Stiring数可指两类数,
它们都是由18世纪的数家James Stiring提出
  第一类:含正负值,其绝对值是包含n个元素的集合分作k个环排列的方法数目,
        递推公式;S(n,0)=0;S(1,1)=1;S(n,k)=S(n-1,k-1)+(n-1)*S(n-1,k)
  第二类:把包含n个元素的集合划分为正好k个非空子集的方法的数目;
        递推公式:s(n,n)=s(n,1)=1;s(n,k)=s(n-1,k-1)+k*s(n-1,k);
        显然,每个Bell数都是“第二类”的和,即B(n)=s(n,1)+s(n,2)+...+s(n,n);
        Bell数和第二类数可以通过构建贝尔三角形a得到。贝尔三角形的形式类似杨辉三角形
        构建如下:
        1、第一行第一项是1(a[1,1]=1);
		2、对于n>1,第n行第一项等同于第n-1行最后一项(a[n,1]=a[n-1,n-1])
		3、对于n,m>1,第n行第m项等于它左边和左上方的两个数之和a[n,m]=a[n,m-1]+a[n-1,m-1];
		结果如下:
		1
		1   2
		2   3   5
		5   7   10   15
		15  20  27   37 52
		.......
		.......
		.......
*/
//UVA 10844
/*
题意是这样的:要求计算一个集合被分成几个不相交子集的方案数,这符合bell数的定义#(n)=B(n);
先通过Bell三角的方法,离线计算出范围内的每个bell数,以后每输入一个n,即可从表中直接的答案
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<time.h>
using namespace std;
__int64 m=10;
int max(int a,int b)
{
	return a>b?a:b;
}
struct Bigint
{
	int l;
	__int64 s[2000];
	void read(__int64 x)
	{
		l=-1;
		memset(s,0,sizeof(s));
		do{
			s[++l]=x%m;
			x/=m;
		}while(x);
	}
	void print()
	{
		printf("%I64d",s[l]);
		for(int i=l-1;i>=0;i--)
		{
			printf("%I64d",s[i]);
		}
	}
}dp[2][1000],ans[1000];
Bigint operator+(Bigint a,Bigint &b)
{
	a.l=max(a.l,b.l);
	__int64 d=0;
	for(int i=0;i<=a.l;i++)
	{
		a.s[i]+=d+b.s[i];
		d=a.s[i]/m;
		a.s[i]%=m;
	}
	if(d) a.s[++a.l]=d;
	return a;
}
int n;
void gettans(int id,int nd)
{
	int i=id^1;
	for(int j=1;j<=nd-1;j++) dp[id][j+1]=dp[i][j]+dp[id][j];
}
void bellWork()
{
	dp[1][1].read(1);
	ans[2]=dp[0][1]=ans[1]=dp[1][1];
	for(int i=2;i<=900;i++)
	{
		gettans(i&1,i);
		dp[(i&1)^1][1]=ans[i+1]=dp[i&1][i];
	}
}
int main()
{
	bellWork();
	printf("time:%d",clock());
	while(~scanf("%d",&n)&&n)
	{
		printf("%d,",n);
		ans[n+1].print();
		printf("\n");
	}
	return 0;
}
//这个运行时间太长了,还待继续研究





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

u014068781

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值