题目链接: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;
}