【u030】扑克牌

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;
}



原文地址:https://www.cnblogs.com/AWCXV/p/7632280.html