/* 两种特殊的排序组合计数公式*/
/*
第一种: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;
}
//这个运行时间太长了,还待继续研究