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題,屬於排列組合+錯排的綜合。