HDU 2068 RPG 的错排(错排问题,组合数)

题目链接:HDU 2068


要求答对一半或以上就算过关,请问有多少组答案能使他顺利过关。

我们倒过来想,求答错一半或以下的组数


错排

错排公式的由来

  pala提出的问题: 十本不同的书放在书架上。现重新摆放,使每本书都不在原来放的位置。有几种摆法?
  这个问题推广一下,就是错排问题: n个有序的元素应有n!种不同的排列。如若一个排列式的所有的元素都不在原来的位置上,则称这个排列为错排。
递推的方法推导错排公式

  当n个编号元素放在n个编号位置,元素编号与位置编号各不对应的方法数用M(n)表示,那么M(n-1)就表示n-1个编号元素放在n-1个编号位置,各不对应的方法数,其它类推.
  第一步,把第n个元素放在一个位置,比如位置k,一共有n-1种方法;
  第二步,放编号为k的元素,这时有两种情况.1,把它放到位置n,那么,对于剩下的n-2个元素,就有M(n-2)种方法;2,不把它放到位置n,这时,对于这n-1个元素,有M(n-1)种方法;
  综上得到
  M(n)=(n-1)[M(n-2)+M(n-1)]
  特殊地,M(0)=1,M(1)=0;


可以看出,我们只要确定哪几个答错了,然后求出这几个的错排数即可, 也就是这几个有多少种可能的排列使它们都不在原来位置上, 就是错排!


组合

答对 i 个人,即答错 n - i 个人,共有C(n, n - i) * M[n - i] 组答案

其中C(n, n - i) 就是从 n 个人选出 n - i 个人的组合数


【源代码】

#include<cstdio>
#include<iostream>
using namespace std;
typedef long long ll;
ll m[20] = {1,0};
ll C(int b,int a){
	ll ans = 1;
	for(int i=1;i<=a;i++){ //求组合数
		ans = ans*(b - i +1)/i;
	}
	return ans;
}
void init(){
	for(int i=2;i<15;i++){ //25 的一半就行
		m[i] = (i-1)*(m[i-2]+m[i-1]); 
	}
	// cout<<m[14]<<endl;
}
int main(){
	int n;
	init();
	while(scanf("%d",&n)!=EOF && n){
		ll ans = 0;
		for(int i=0;i<=n/2;i++){ // 选错0 个 到 选错一半求和。
			ans += C(n,i)*m[i];
		}
		cout<<ans<<endl;
	}
	return 0;
}



你可能感兴趣的:(ACM,数学题,错排)