Description
今天西工大举办了一场比赛总共有m+n人,但是有m人比较懒没带电脑,另外的n个人带了电脑。不幸的是,今天机房的电脑全坏了只能用带的电脑,一台电脑最多两人公用,确保n>=m。但是大家来的时间不同,随机次序来机房,带电脑的人直接准备比赛而没带电脑的人需要向带电脑并还没和别人公用的人求助(当然会答应)。但是,如果不存在带电脑并还没和别人公用的人,那他就要等了,等是很让人头疼的,这就不和谐了,当然假如没有这样的情况发生比赛是很和谐的。
Input
输入多组数据,每组数据只有一行m(1<=m<=n<=20)和n(1<=n<=20);
Output
输出和谐比赛的场数。
Sample Input
1 2 3 8 4 17
Sample Output
2 110 4655这道题可以看成是递推,也可以用组合数学来解,具体就是卡特兰数。我们把没带电脑的看成0,带电脑的看成1,那么问题就转化成从左向右扫描0的个数不超过1的个数假设某一位0之前0的累计数等于1的累计数,假设之前有m个1,m个0,这个0是第2m+1位,之后有2n-m个1和2n-m-1个0,要出现0的累计数大于1的累计个数这种情形,我们可以增加0的个数,类似于DP那种可以转化问题的思想,但是怎么转化成我们要求解的问题呢?我们可以将2n-m个1变成0,将2n-m-1个0变成1,转化后就会出现0的累计数大于1的累计个数这种情形。答案就是卡特兰数c(q,p+q)-c(q+1,p+q);q代表0的个数。类似于这种转化成01形式求解的还有:给定一个4*3的棋盘,从左下角到右上角,每次只能向右走或者向上走,有多少种方案?我们发现要从左下角到右上角只要向右走四步,向上走三步,用1代表向上走,用0代表向右走,那么问题就转化成由4个0和3个1组成的7位01字符串,这两种形式一一对应,答案就是c(4,7).
#include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <cmath> #include <algorithm> #include <set> #include <map> #include <queue> #include <string> #define LL long long using namespace std; int p, q; LL f[25][25]; void Init() { memset(f, 0, sizeof(f)); for (int i = 0; i <= 20; ++i) { f[i][0] = 1; } for (int i = 1; i <= 20; ++i) { for (int j = 1; j <= 20; ++j) if(i<j) f[i][j]=0; else f[i][j] = f[i-1][j] + f[i][j-1]; } } LL c[100][100]; void slove() { memset(c,0,sizeof(c)); for(int i=1;i<=100;i++) { c[0][i]=1; c[1][i]=i; c[i][i]=1; } for(int i=2;i<=100;i++) for(int j=2;j<=100;j++) c[j][i]=c[j][i-1]+c[j-1][i-1]; } double C(int m,int n) { double answer1=1,answer2=1; for(int i=n-m+1;i<=n;i++) answer1*=i; for(int i=1;i<=m;i++) answer2*=i; return answer1/answer2; } int main() { //freopen("test.in", "r", stdin); slove(); while (scanf("%d%d", &p,&q) != EOF) { printf("%lld\n",c[q][p+q]-c[q+1][p+q] ); printf("%.0lf\n",C(q,p+q)-C(q+1,p+q) ); Init(); printf("%lld\n", f[q][p]); } return 0; }
时间: 2024-10-03 15:01:28