题目链接
今年暑假杭电ACM集训队第一次组成女生队,其中有一队叫RPG,但做为集训队成员之一的野骆驼竟然不知道RPG三个人具体是谁谁。RPG给他机会让他猜猜,第一次猜:R是公主,P是草儿,G是月野兔;第二次猜:R是草儿,P是月野兔,G是公主;第三次猜:R是草儿,P是公主,G是月野兔;......可怜的野骆驼第六次终于把RPG分清楚了。由于RPG的带动,做ACM的女生越来越多,我们的野骆驼想都知道她们,可现在有N多人,他要猜的次数可就多了,为了不为难野骆驼,女生们只要求他答对一半或以上就算过关,请问有多少组答案能使他顺利过关。
Input
输入的数据里有多个case,每个case包括一个n,代表有几个女生,(n<=25), n = 0输入结束。
Output
1 1
Sample Input
1 2 0
Sample Output
1 1
PS:中文题,题意不用说了,本题需要用到错排公式和组合数。
错排公式
所谓错排就是一列数据,每个数都有自己的编号,错排简言之就是排序后每个数都不在自己位置上(应该很好懂吧)。
我们现在以s本书为例,书的编号(1-s),设D(s)为s本书时的错排序列数,D(s-1)为s-1本书的错排排序数。
首先我们从s本书里面挑出一本书编号为m,现在我们把它放在编号为n的书的位置,这样一共有(s-1)种放法,挑出来编号为n的书放回有两种情况:
第一种情况:把编号为n的书放在空出的m的书的位置上,这样两本书错位了,满足错排的要求。然后剩下s-2本书的位置不动,对剩下的s-2本书进行错排,我们的问题又回到开始那样求D(s-2),所以这种情况的错排的排序序是(s-1)*D(s-2)。
第二种情况:把编号为n的数不放在空出的编号为m书的位置上,等于现在我们要从除去m,n这两本书的位置,在剩下s-2个位置中选一个。现在我们可以想一个问题,现在我们编号为m的书已经不用管了,现在还要放没有放回的这一本,加上还没有选的s-2本书,总共s-1本书。这是不是相当于除去在前面已经错位编号为m的书,对剩下的n-1本书进行错排。所以这种情况错排的排序数为(n-1)*D(n-1)。
所以错排的递推公式为:D(n)=(n-1)*(D(n-1)+D(n-2))。
对于这个题,他是要求大于一半排序正确的情况数,我们可以转化成求小于一半数的错排序列和,这样我们只要求出1-n/2本书的错排的序列和就就行了,但假如我们是在n个女生中选出任m个女生进行错排,所以我们还要乘以一个C(n,m)。
细节看代码:
#include <iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<map>
#include<queue>
#include<set>
#include<cmath>
#include<stack>
#include<string>
const int maxn=1e2+10;
const int mod=10007;
const int inf=1e8;
#define me(a,b) memset(a,b,sizeof(a))
#define lowbit(x) x&(-x)
typedef long long ll;
using namespace std;
ll num[maxn];
void inct()//排错公式
{
num[1]=0,num[2]=1;
for(int i=3;i<15;i++)
num[i]=(i-1)*(num[i-1]+num[i-2]);
}
ll get_C(int n,int m)//求组合数的值
{
ll sam=1;
for(int i=1;i<=m;i++)
{
sam*=(n-i+1);
sam/=i;
}
return sam;
}
int main()
{
inct();
int n;
while(scanf("%d",&n)&&n)
{
ll sum=1;
for(int i=1;i<=n/2;i++)
sum+=num[i]*get_C(n,i);//i个女生序号错的,还要乘上选出i个女生的组合数。
printf("%lld\n",sum);
}
return 0;
}