在实现组合数计算的时候要防止溢出

1. 在计算组合数C(N, M)的时候如果利用公式n!/(m!*(n-m)!)的话,很可能会溢出。

因为对于阶乘,13!已经超过了int能表示的范围,而且也会很快超过long long的表示范围。


2. 如果按照定义先计算分子,再计算分母,再相除的话也会溢出。


3. 最保险的计算方式如下:

分子:N*(N-1)*...*(N-M+2)(N-M+1)

分母:M*(M-1)*...*2*1


从后往前计算,先计算(N-M+1)/1=x1,再计算(N-M+2)*x1/2=x2,再计算(N-M+3)*x2/3,这样能防止溢出,代码如下:(这样计算能够保证每次的计算结果都是整数,因为每次的计算结果就是一个组合数)

当然如果本来组合数的大小就超过long long能表示的范围,那下面的代码也不行了。


#include <iostream>
#include <iomanip>
#include <cmath>
#define PI 3.1415927
using namespace std;

//avoid use fac to calculate c(n,m)
//fac will overflow
long long C(int N, int M) {
	long long sum = 1;
	for(int i=1;i<=M; i++) {
		sum=sum*(N-M+i)/i;
	}
	return sum;
}

//C(N, M)*(M个元素错排的总数)
long long func(int N, int M) {
	long long num[51]={0};
	num[0]=1;//0个元素错排,即全部排对了,只有1种可能
	num[1]=0;
	num[2]=1;
	for(int i=3; i<=M; i++) {
		num[i]=(i-1)*(num[i-1]+num[i-2]);
	}
	return C(N,M)*num[M];	
}

int main()
{ 
	int n;
	while(cin >> n) {
		if(n==0) break;
		long long sum = 0;
		for(int i=0; i<=n/2; i++) {//0,1 to n/2 错排 是符合要求的
			sum+=func(n,i);
		}
		cout << sum << endl;
	}
	return 0;
}

上面的代码来源于HDUOJ2068题,属于排列组合+错排的综合。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值