oj之路(第一天)(续:由题目看思想)

来源:互联网 发布:快速排序java实现 编辑:程序博客网 时间:2024/05/21 14:09

我又来了,这是今天最后一道题咯~~作为新手,一天打这么多~这么久~的代码,还是真的觉得神疲力竭啊~

好了,接下来要说的一道题,应该只能说是一道数学题,不过数学题也是能够体现一些思想滴~~好好琢磨才是王道

===========================================================================================================

Description
    一天,OYY 从外面打完比赛回来,手上拿了很多个气球,颜色各不相同。他见到我,就说,你看,我拿了很多气球!
我膜拜死了!!然后他就问了我一个问题,如果把这里的气球分成若干份。有多少种分法呢?
    由于我数学非常菜,顿时头晕了,因此希望大家能帮我解答这个问题(@_@))
输入格式
输入数据有2行
第1 行有两个数n,m,分别代表oyy 手上的气球个数和分的份数(n<=10,m<=5)
第2 行有m 个数,分别代表每一份的个数,保证总个数等于n
输出格式
输出数据有1行,输出一个数代表不同分法的总数。
输入样例
3 1
3
输出样例
1
提示
Sample Input2:
4 2
2 2
Sample Output2:
3

===========================================================================================================

思考过程:学习过高中课程的大概都知道这种题的解法了吧,揭开那神秘的面纱,展现在眼前的就是--排列组合的问题
但是这里我想所说的是:
任何心里觉得有底气的题目就越是要小心仔细的思考一些细节,像我抓狂还是被这道题目折磨了一段时间的。以下时要注意的问题,也是算法:
对组合的拆分要仔细分析,如果出现拆分出来的个数是一样的,那么直接进行全部组合的统计将会导致重复计数的情况(即直接用C几几这样的运算)
不过这样计算完也是没错的,只要接下来进行重复情况的去除就行了--这就是这道题的算法--先进行所有组合的统计,之后再来去除重复计数。

以上的算法基于这样一个定理:对一个数n进行平均拆分成m份(整数),那么结果会有这么多的情况(都不一样):Cnm / Amm(第一个在右下角,第二个在右上角)

所以想要去除重复计数就很简单了:
如果拆分时没有出现任何一样的份数(例如:1,2,3,4),那么就不用去除重复计数,因为压根就没有重复计数。
如果出现了重复计数m1份,m2份等等(例如:1,1,3,3),那么把结果除以m1的阶乘,m2的阶乘等等就得到答案了。

===========================================================================================================

#include <stdio.h>int C(int a, int b){int tmp = 1;for (int i = 1; i <= a; ++i)tmp *= b--;for (int i = 1; i <= a; ++i)tmp /= i;return tmp;}int fact(int div){int sum = 1;while (div)sum *= div--;return sum;}int main(){int sum = 1;int n, m, mt[5] = { 0 };scanf("%d%d", &n, &m);for (int i = 0; i < m; ++i) {scanf("%d", &mt[i]);}for (int i = 0; i < m; ++i) {sum *= C(mt[i], n);n -= mt[i];}for (int i = 0; i < 4; ++i) {int divisor = 1;for (int j = i + 1; j < 5 && mt[i] != 0; ++j) {if (mt[i] == mt[j]) {divisor++;mt[j] = 0;}}sum /= fact(divisor);}printf("%d\n", sum);return 0;}

===========================================================================================================


0 0