计算组合数

编写函数,参数是两个非负整数n和m,返回组合数

其中m<=n<=25。例如,n=25,m=12时的答案为5200300。

代码及算法分析

程序4-1 组合数(有问题)

#include <stdio.h>
long long factorial(int n)
{long long m=1;//不要忘记初始化,不然的出来的结果惊人。for(int i=1;i<=n;i++){m*=i;}return m;
}
int main ()
{int n,m;scanf("%d %d",&n,&m);printf("%lld",factorial(n)/(factorial(m)*factorial(n-m)));return 0;
}

这个代码的问题显而易见,阶乘容易溢出,所以不可取。
所以,套用刘汝佳老师的一句话:”即使最终答案在所选择的数据类型范围之内,计算的中间结果仍然可能溢出“。

汝佳老师也给出了相应的解决方案,虽然不能完全避免中间结果溢出,但是对于题目给出的范围已经可以保证得到正确的结果了。
先来分析一下组合数的公式:
n!
————
m!(n-m)!
展开之后:
n *(n-1) *(n-2)……3 *2 *1
—————————————————————————————(1)
m *(m-1) *(m-2)……3 *2 *1 *(n-m) *(n-m-1) *(n-m-2)…… *3 *2 *1
因为n>=m,所以m在1~n之间,这样可以把n!除以m!约分:
n *(n-1) *(n-2)……(m+2) *(m+1)
————————————————(2)
(n-m) *(n-m-1) *(n-m-2)…… *3 *2 *1

然后汝佳老师给了一道思考题:为什么当m<n-m时要把m变成n-m?
移项之后得到:m<n/2,先列一个数轴:
|————————————————————>
0 1 2 3 ……m……n/2……M……(n-1) n
为了不把变换后的m与变换前的m弄混,把变化后的m记为M。因为组合数的公式,分子是大于分母的,所以分子的乘积比较大,容易溢出,要想优化,就得让分子乘积变小。
M=n-m带入组合式公式之后就是:
n!
————————————————(3)
M!(n-M)!=(n-m)!(n-(n-m))!=(n-m)!m!
所以式子并没有变化,同样可以进行约分得到(2)式的变式:
n *(n-1) *(n-2)……(M+2) *(M+1)
————————————————(4)
(n-M) *(n-M-1) *(n-M-2)…… *3 *2 *1
相当于将数轴
|————————————————————>
0 1 2 3 ……m ……n/2……M ……(n-1) n
标出的部分直接干掉了。

由于M是大于m的,所以分子的乘积缩小,溢出问题得到缓解,注意,是缓解不是解决,如果n和m的值很大的话还是会溢出。
分析之后就可以把程序4-1升级为程序4-2了:

#include <stdio.h>
long long c(int n,int m)
{if(m<n-m){m=n-m;}long long ans=1;for(int i=m+1;i<=n;i++){ans*=i;}for(int i=1;i<=n-m;i++){ans/=i;}return ans;
}
int main ()
{int n,m;scanf("%d %d",&n,&m);printf("%lld",c(n,m));return 0;
}

汝佳老师提示

“对复杂的表达式进行化简有时不仅能减少计算量,还能减少甚至避免中间结果的溢出。”

《算法竞赛入门经典》计算组合数问题相关推荐

  1. 刘汝佳《算法竞赛入门经典》---总结

    刘汝佳:<算法竞赛入门经典> 三步: 基本的数据结构+算法知识: 数论等数学基本知识: 锻炼联想建模能力.知识与实际相结合,解决实际问题! 第一章:程序设计入门 1.a/b 当a.b为整数 ...

  2. 算法竞赛入门经典(刘汝佳)——代码笔记

    Reference: <算法竞赛入门经典>(刘汝佳)第一版.第二版 ------------------------------------------------------------ ...

  3. 《算法竞赛入门经典训练指南》pdf

    下载地址:网盘下载 基本介绍 编辑 内容简介 <算法竞赛入门经典:训练指南>题目多选自近年来ACM/ICPC区域赛和总决赛真题,内容全面,信息量大,覆盖了常见算法竞赛中的大多数细分知识点. ...

  4. 算法竞赛入门经典训练指南

    最近在看算法竞赛入门经典训练指南这本书,书中不错的算法我将在博客中发布,和大家共同学习. 题目: 在你的王国里有一条n个头的恶龙,你希望雇一些骑士把它杀死(即砍掉所有头).村里有m个骑士可以雇佣,一个 ...

  5. 算法竞赛入门经典 习题3-2 分子量 Molar Mass

    给出一种物质的分子式(不带括号),求其分子量.本题分子式中只包含四种原子,分别为C.H.O.N,原子量分别为12.01,1.008,16.00,14.01.例如,C6H5OH的分子量为94.108g/ ...

  6. 算法竞赛入门经典(第二版)第三章习题

    声明:作者水平有限,只是会基础C语言的小菜,C++还未入门.作者仅根据算法竞赛入门经典(第二版)书上第三章习题所述题意而编写,并未严格按照原题的输入输出编写,代码仅经过个人测试(OJ网站太慢了).代码 ...

  7. 《算法竞赛入门经典——训练指南》第一章相关内容

    #<算法竞赛入门经典--训练指南>第一章相关内容 希望各位大牛能指导! 红色为已经做了的...黄色背景是还有不懂地方,希望在年前能刷完第一章啊.... 更新版.google上貌似又加了ex ...

  8. 第一章 程序设计入门--算法竞赛入门经典

    第一章 程序设计入门–算法竞赛入门经典 知识点一: int m=25; printf("%d\n",m); printf("%03d\n",m); 输出如下: ...

  9. #《算法竞赛入门经典》勘误

    转自http://code.google.com/p/aoapc-book/wiki/BeginningAlgorithmContestsErrata #<算法竞赛入门经典>勘误 关于勘误 ...

  10. 算法竞赛入门经典——训练指南

    <算法竞赛入门经典--训练指南> 基本信息 作者: 刘汝佳 陈锋 [作译者介绍] 丛书名: 算法艺术与信息学竞赛 出版社:清华大学出版社 ISBN:9787302291077 上架时间:2 ...

最新文章

  1. 释放变量所指向的内存_C++动态内存分配(学习笔记:第6章 15)
  2. TiDB 在小红书从 0 到 200+ 节点的探索和应用
  3. 无锡朗贤获B+轮融资,辰韬资本、兴韬投资领投
  4. OpenCV Error: Sizes of input arguments do not match (The operation is neither 'array op array' (wher
  5. ADO.NET 核心对象简介
  6. $_server['php_self'] 漏洞,Discuz! $_SERVER['PHP_SELF'] XSS Vulnerability
  7. Redis使用过程出现类型转换异常问题- 20190220
  8. 敏捷开发免费管理工具——火星人预览之五:常见问题问答
  9. Vlan配置详解之三层交换
  10. 考勤排班_考勤管理系统VS传统考勤排班优劣如何?
  11. Swf Decrypt详解
  12. xp系统简单tcpip服务器,XP系统怎样安装TCP/IP协议
  13. ps—图层、(移动工具中)对齐
  14. 挑食有理——罗敏娜集团总裁卓顺发养生之道
  15. 通过爬虫获取第五人格游戏信息整理并分析(一)
  16. 关于DoEvents
  17. BBR 拥塞控制算法blog笔记
  18. imvu为什么显示无法连接服务器,IMVU服务器错误怎么办 服务器无法连接解决办法...
  19. 【C++代码】区间重叠问题
  20. 清闲的工作与温水煮青蛙;-)

热门文章

  1. 简单来说一下java中的泛型,ssh中dao层使用会简化代码量
  2. (MoMoCMS教程11)页面的SEO优化与外链
  3. (转)用来理解Java的8个图表
  4. html多个盒子重叠浮动,如何让浮动后的多个盒子水平居中
  5. java Date days_Java中的LocalDate plusDays()方法
  6. ios获取新数据要不要关_ios获取新数据要不要关
  7. 判断是否是数组的方法
  8. Java黑皮书课后题第5章:5.12(求满足n方>12000的n的最小值)使用while循环找出满足n方大于12000的最小整数n
  9. Java黑皮书课后题第2章:*2.19(几何:三角形面积)编写程序,提示用户输入三角形的三个点(x1, y1)(x2, y2)(x3, y3),然后显示它的面积
  10. Linux shell去除字符串中所有空格