Time Limit: 1 second
Memory Limit: 128 MB
【问题描述】
组合数学是数学的重要组成部分,是一门研究离散对象的科学,它主要研究满足一定条件的组态(也称组合模型)的存在、计数以及构造等方面的问题。组合数学的主要内容有组合计数、组合设计、组合矩阵、组合优化等。 随着计算机科学的日益发展,组合数学的重要性也日渐凸显,因为计算机科学的核心内容是使用算法处理离散数据。 今天我们来研究组合数学中的一个有趣的问题,也是一个简单的计数问题: 从一副含有n(n≤10000)张的扑克牌[显然每张扑克牌都不相同]中,分给m(m≤100)个人,第i个人得到ai (0≤ai≤100)张牌,求一共有几种分法,这个数可能非常大,请输出此数模10007后的结果。【输入格式】
第一行两个整数 为 n m 第二行 m个整数 ai
【输出格式】
【数据规模】
Sample Input1
5 2 3 1
Sample Output1
20
Sample Input2
20 19 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
Sample Output2
8707
【题解】
这是一道组合的问题。
难点在组合数的求法(除法可没有同余率可以用!)。
设c[n][m]为从n个数中取m个数的排列。
则有递推关系c[n][m] = c[n-1][m-1] +c[n-1][m];
(杨辉三角)
然后再加上一句%10007即可。
边界条件是c[n][0] = 1;
c[n][n] = 1;
因为最多取100张牌,所以第二维只要开到101就可以了。
然后对于每一个人的需求ai;
答案先乘上c[n][ai],然后n-=ai;
n就变成剩余的纸牌数量了,然后重复上述乘法过程即可。
【代码】
#include <cstdio> int n, m,a[101]; int c[10001][101] = { 0 }; void init() { c[0][0] = 1; for (int i = 1; i <= 10000; i++)//这是边界条件。 { c[i][0] = 1; if (i <= 100) c[i][i] = 1; } for (int i = 1; i <= 10000; i++) //获得c[0..10000][0..100] { int to; if (i < 100) to = i; else to = 100; for (int j = 1; j <= to; j++)//杨辉三角递推式。 c[i][j] = (c[i - 1][j] + c[i - 1][j - 1])%10007; } } int main() { init(); scanf("%d%d", &n, &m); for (int i = 1; i <= m; i++)//输入n个需求量 scanf("%d", &a[i]); __int64 temp = 1; for (int i = 1; i <= m; i++)//对每一个需求量用组合原理进行乘法运算(分步相乘->分步来给扑克牌); if (n >= a[i]) { temp = (temp*c[n][a[i]])%10007; n -= a[i]; } else //如果不够分了,方案则为0.输出答案,结束程序。 { temp = 0; break; } printf("%I64d", temp);//输出答案。 return 0; }