[ 不容易系列之(4)——考新郎]+(错排的简单理解+改进计算组合数)

不容易系列之(4)——考新郎
问题可以简单描述为从N对新婚夫妇,从中抽取M个新郎,这M个新郎都找错自己对象的结果组合。

  1. 首先要明白从中抽取M个新郎是组合问题,我们可以用到组合公式在这里插入图片描述
    从N个元素里抽取N个进行组合;

  2. 还有要错排,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循环的次数。
在这里插入图片描述

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值