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题,属于排列组合+错排的综合。