不容易系列之(4)——考新郎
问题可以简单描述为从N对新婚夫妇,从中抽取M个新郎,这M个新郎都找错自己对象的结果组合。
-
首先要明白从中抽取M个新郎是组合问题,我们可以用到组合公式
从N个元素里抽取N个进行组合; -
还有要错排,M个新郎找错了新娘,所以要用到错排公式。
3如何理解错排?
l
我们可以简单理解为:现在有5个数已经从小到大排好了,如 1 2 3 4 5,现在要找让它们打乱且 全部都不能在之前的位置有多少种排法。我们可以设:当n个编号元素放在n个编号位置,元素编号与位置编号各不对应的方法数用D(n)表示,那么D(n-1)就表示n-1个编号元素放在n-1个编号位置,各不对应的方法数,其它类推.,对于我们这里给的例子可以看为n=5,(即有5个元素),首先将其中一个元素例如:编号5打乱顺序,就有n-1种(即4种排法),更具体一步,我们可以理解为(情况1,编号5与其中一个元素互相交换位置)存在第一次打乱后排序是这样的1 2 5 4 3,因为此时编号3与编号5已经是属于错排状态,所以剩下(n-2)个元素属于原来的状态,所以有D(n-2)种排法。还有(情况2)编号3不与编号5换,即除去编号5后,剩下的4个元素自己错排,即有D(n-1)种排法,所以总排法=(n-1)*(情况1排法+情况2排法);
公式总结为:D(n) = (n-1) * ( D(n-1) + D(n-2) )
其中D(1)=0,D(2)=1,n为现在需要错排的元素数量。
#include<iostream>
using namespace std;
long long int D[1000];//保存错排数
const int MAX=25;
int zu_he_shu(int n,int m)
{
int i;
long long int ans=1;//int 也行,因为题目N最大不超20;
if(m<n-m){
m=n-m; //改进后的组合数算法,通过预先约分减少计算次数(即约去了 m!)
}
for(i=m+1;i<=n;i++){
ans=ans*i; //计算组合数的分子
}
for(i=1;i<=n-m;i++){
ans=ans/i; //计算约分分母(n-m)!
}
return ans;
}
void cuo_pai()
{
int i;
for(i=3;i<=MAX;i++){ //注意i从3开始
D[i]=(i-1)*(D[i-1]+D[i-2]);
}
}
int main()
{
D[1]=0; //注意对错排序数前2个初始化。
D[2]=1;
cuo_pai();
int t,n,m;
cin>>t;
while(t){
t--;
cin>>n>>m;
cout<<D[m]*zu_he_shu(n,m)<<endl;
}
}
4.下面图片是对组合数第一个for循环打法的理解。
同时尽量m要大一点,减少第一个for循环的次数。