有趣的组合问题
-
描述
-
有n 种颜色的乒乓球,每种颜色的乒乓球的个数有任意多个,从中取m个球,问一共有多少种取法。
-
输入
- 每行2个正整数 n,m,(n,m)<32,以n和m都等于0结束。 输出
- 每组输出占一行,输出一共有多少种取法 样例输入
-
3 3 3 2 2 2 0 0
样例输出
-
10 6 3
组合数方法:
运用隔板法理解 。例如3, 3这组数据, 就是把三个球用两个隔板分成三个部分,用R, B, G代表三种不同的颜色。10中情况如下:
| | G G G | B | G G | B B | G | B B B |
R | | G G R | B | G R | B B |
R R | | G R R | B | R R R | |
也就是说第一个隔板的左边是R颜色的球, 第一个和第二个隔板的中间是B颜色的球,第二个隔板的右边是G颜色的球
所以就是在5个位置上枚举2个隔板的不同位置就可以了。
所以3, 3这个数据的答案就是C(5, 2)
同样的n种颜色取m个球,就是在n + m + 1个的位置中,枚举n - 1个隔板的位置,所以答案就是C(m+n-1,n-1)=C(m+n-1,m).
接下来就是计算组合数的问题了。
我开始想的是将1-64的阶乘存在数组里(a[i] = a[i - 1] *i),但是数组太大 超过了long long所以采用下面的方法
数组a[i][j]代表 C(i, j) ,C(i, j) = C(i - 1, j) +C(i - 1, j - 1)
#include <stdio.h>
long long a[64][64];
int main (void)
{
int m, n, i, j;
a[0][0] = a[1][0] = a[1][1] = 1;
for(i = 2; i < 64; i++)
{
a[i][0] = 1;
for(j = 1; j <= i; j++)
{
a[i][j] = a[i - 1][j] + a[i - 1][j - 1];
}
}
while(scanf("%d %d", &n, &m) != EOF)
{
if(m == 0 && n == 0)
break;
printf("%lld\n", a[m + n - 1][m]);
}
return 0;
}
动规:
dp[i][j] = dp[i - 1][j] + dp[i][j - 1].当从i种颜色里面取j个球的时候,分为是否包含第i种:
当至少含有一个第i种时,方法数就是在i种种选j - 1个(因为要保证至少有一个是第i种)
当不含有第i种的时候,方法数就是在i - 1中里面选出j个。
感觉有点像整数划分
#include <stdio.h>
long long dp[35][35];
//dp[n][m]表示n种颜色,取m个球
int main (void)
{
int i, j;
for(i = 0; i < 35; i++)
{
dp[1][i] = 1;//一种颜色取i个球额,只有一种取法
dp[i][1] = i;//i种颜色取1个球,有i种取法
}
for(i = 2; i < 35; i++)
{
for(j = 2;j < 35; j++)
{
dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
}
}
int n, m;
while(scanf("%d %d", &n, &m) != EOF)
{
if(n == 0 && m == 0)
break;
printf("%lld\n", dp[n][m]);
}
return 0;
}