acm新手训练之卡特兰数(hdu2067)

小兔的叔叔从外面旅游回来给她带来了一个礼物,小兔高兴地跑回自己的房间,拆开一看是一个棋盘,小兔有所失望。不过没过几天发现了棋盘的好玩之处。从起点(0,0)走到终点(n,n)的最短路径数是C(2n,n),现在小兔又想如果不穿越对角线(但可接触对角线上的格点),这样的路径数有多少?小兔想了很长时间都没想出来,现在想请你帮助小兔解决这个问题,对于你来说应该不难吧!
Input
每次输入一个数n(1<=n<=35),当n等于-1时结束输入。
Output
对于每个输入数据输出路径数,具体格式看Sample。
Sample Input
1
3
12
-1
Sample Output
1 1 2
2 3 10
3 12 416024

根据题意路径不能切过对角线,故用对角线将棋盘分成两半,求出其中一半的路径数再乘以2即可;
现取一半进行分析,即三角形:
将对角线上的点表为0,1,2。。。n;
再做一条平行于对角线的一条直线并向下平一个单位,记为0,1,2,3,4,5。。。n-1;
设h(n)为走到点n的路径数;
为了避免重复,h(m)*h(n-m-1)表示第一次碰到对角线是碰到了对角线m+1的路径数,因为第一次碰到对角线,即走到对角线m+1之前走过的路径都不能碰到对角线,则此次走到对角线m+1的前一步必定为走到平行线的m;
所以总路径数可分为第一次碰到的对角线为1,第一次碰到的对角线为2,第一次走到对角线为点3。。。第一次碰到的对角线点为n-1,所以h(n)=h(0)*h(n-1)+h(1)*h(n-2)+…+h(n-1)*h(0);
到此问题全部解决
ac代码

#include<iostream>
#include<cstring>
#include<string>
using namespace std;
long long p[40];
void ask()
{
	p[0] = 1; p[1] = 1;
	for (int i = 2; i <= 35; i++)
		for (int j = 0, l = i - 1; j < i ; j++, l--)
			p[i] += p[j] * p[l];
}
int main()
{
	int n, j = 1;
	ask();
	while (cin >> n)
	{
		if (n == -1)break;
		cout << j << " " << n << " ";
		cout << 2 * p[n] << endl;
		j++;
	}

}

此结果其实就是卡特兰数*2。
卡特兰公式有:
令h(0)=1,h(1)=1,Catalan数满足递推式:h(n)= h(0)*h(n-1)+h(1)*h(n-2) + … + h(n-1)*h(0) (n>=2)
例如:

h(2)=h(0)h(1)+h(1)h(0)=11+11=2
h(3)=h(0)h(2)+h(1)h(1)+h(2)h(0)=12+11+21=5
另类递推式:h(n)=h(n-1)(4n-2)/(n+1);
递推关系的解为:h(n)=C(2n,n)/(n+1) (n=0,1,2,…)

猜你喜欢

转载自blog.csdn.net/weixin_43965698/article/details/86619092