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