错排

Problem Description
国庆期间,省城HZ刚刚举行了一场盛大的集体婚礼,为了使婚礼进行的丰富一些,司仪临时想出了有一个有意思的节目,叫做"考新郎",具体的操作是这样的:
错排_第1张图片

首先,给每位新娘打扮得几乎一模一样,并盖上大大的红盖头随机坐成一排;
然后,让各位新郎寻找自己的新娘.每人只准找一个,并且不允许多人找一个.
最后,揭开盖头,如果找错了对象就要当众跪搓衣板...

看来做新郎也不是容易的事情...

假设一共有N对新婚夫妇,其中有M个新郎找错了新娘,求发生这种情况一共有多少种可能.
 

Input
输入数据的第一行是一个整数C,表示测试实例的个数,然后是C行数据,每行包含两个整数N和M(1<M<=N<=20)。
 

Output
对于每个测试实例,请输出一共有多少种发生这种情况的可能,每个实例的输出占一行。
 

Sample Input
     
     
     
     
2 2 2 3 2
 

Sample Output
     
     
     
     
1 3

Description

今年暑假杭电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输入结束。
 

Sample Input

       
       
       
       
1 2 0
 

Sample Output

       
       
       
       
1 1
                   
【思路分析】

这是两道关于错排的问题的题目。先说一下错排的原理:举一简单的列子,给出5把椅子,分别编号为1~5,然后又有5个人,每人对应的编号也是1~5,问题:让5个人都随意找个座位坐下,求5个人所坐椅子位编号都不与自己的编号一样的坐法有多少种。

首先,我们让5个人中任意一个人先选择,因为不能选择和自己编号一样的椅子,所以这个人有4中选法,即(n-1)种选法,至于这个人的编号是几,他选的椅子编号又是几,我们不用去死抓,至于为何了,假设让1号先选择,他选择的椅子无非是2~5号中的一把,然后又让2号去选择,此时问题在于,1号究竟选择了2~4号中的哪一把椅子了,如果1号选择了2号椅子,那么此时2号就有1、3、4、5这4种选择,那如果1号没有选择2号,而是选择了3、4、5这3种中的一种了,岂不是2号此时只有3总选择了?我们继续假设,我们先假设1号选的就是2号椅子,此时,2号就有了4种选择,2号选择完后,我们继续让3号选择,同2号选择的时候一样,此时,我们又得纠结2号究竟选择了1、3、4、5中的哪一把椅子,若果2号选择了3号椅子,那么3号此时就有1、4、5这3中选择,而如果2号没有选择3号椅子,3号又岂不是只有2中选择了?我们继续假设,2号选择的就是3号椅子,以下的思路同上,直到5号选择完为止。此时我们将1号选择2号椅子、2号选择3号椅子、3号选择4号椅子、5号选择1号椅子这种想法放下,我们回到1号选择时的最初位置,接下来,我们又继续假设,因为我们假设了1号选择了2号椅子,所以,我们这次就假设1号没有选择2号椅子而是选择了3、 4、 5号椅子中的一把,然后又让2号选择,此时,2号剩下1、 4、 5号或1、 3 、 5号或1、 3、 4号这几种可能中去选择一把椅子,2号选择完后,3号继续选择,同一号是选择一样,2号也没有选择3号,所以,此时3号就剩下4、 5号或1、 5号或1、 4号这几种中去选择一把椅子,以下规律同上,当我们将这两种可能性想完过后,发现这道题挺难的,过程十分的麻烦,其实不然,我们假设的是让1号先选择,所以我们也可以假设让2、 3、 4、 5号先选择啊,所以以上所讨论的复杂问题就因此迎刃而解了,应为5个人都可以作为第一个选择的人,他们的选择即是独立的又互相影响着,所以,我们不用纠结谁先选,先选的人又选了那一把椅子。继续就这一问题作答,5个人,第一次选择的人有(5-1)种选择方法,剩下的4个人,我们不急着让他们去选择,而是将这4个人新的整体来考虑,此时这4个人种又得有一个人先选择,此时有(4-1)种方法,同第一次选择过后,我们又将剩下的3个人看成一个新的整体,此时3个人中第一个人又有(3-1)中选法,用这种方法直到最后一人选择完毕,此时我们发现:第5个人(不是5号)选择的时候依赖于前4个人的选择,而第4个人的选择又依赖于前3个人的选择,而第3个人(不是3号)的选择又依赖于前3个人,第2个人(不是2号)选择的时候又依赖于第一个人的选择,如此一来,就形成了一个递归问题, 如果把数字换成N、 N -1、 N-2、 ……3、 2 、 1,此时我们可以用这样一个式子表示: f(N) = (N-1)(f(N-1) + f(N-2)),f(N)代表有N个人时的选法。以上论述也许还存在许多瑕疵,如有论述不当之处请多多指教。

有了递归公式和错排思想过后,我们做着两道题就简单多了,接下来就不多说了,我们直接见代码:

【考新郎】

方法一:

#include <iostream>
#include <algorithm>
#include <cstring>
#include <stdio.h>
using namespace std;
int fun(int m,int n)
{
    __int64 sum, sum1 = 1, sum2 = 1;
    for(int i = m; i > n; i--)
        sum1 *= i;
    for(int j = 1; j <= m - n; j++)
        sum2 *= j;
    sum = sum1 / sum2;
    return sum;
}
int main()
{
    int c, m, n;
    __int64 way[21][21];
    way[0][0] = 0;
    way[1][1] = 0;
    way[2][1] = 0;
    way[2][2] = 1;
    for(int i = 3; i <= 20; i++)
        for(int j = 2; j <= i; j++)
        {
            if(j == 2)
                way[i][j] = fun(i,j);
            else
                way[i][j] = fun(i,j)*(j - 1)*(way[j - 1][j - 1] + way[j - 2][j - 2]);
        }
    cin >> c;
    while(c--)
    {
        cin >> m >> n;
        cout << way[m][n] << endl;
    }
    return 0;
}//此种方法不建议采用,操作有点复杂

方法二:

#include <stdio.h>
int main()
{
int n,m,c,i;
    scanf("%d",&c);
while(c--)
{
__int64 a[31];
a[0]=a[1]=0;a[2]=1,a[3]=2;
for(i=4;i<30;i++)
a[i]=(i-1)*(a[i-1]+a[i-2]);
__int64 sum1,sum2;
sum1=sum2=1;
scanf("%d%d",&n,&m);
for(i=n;i>n-m;i--)
sum1*=i;
for(i=2;i<=m;i++)
sum2*=i;
printf("%I64d\n",sum1/sum2*a[m]);


}
}


【RPG错排】

#include <iostream>
#include <algorithm>
using namespace std;
int fun(__int64 a,__int64 b)
{
    __int64 sum = 1;
    for(__int64 i = 0; i < b; i++)
        sum = sum * (a - i) / (i + 1);
    return sum;
}
int main()
{
    __int64 n,m,way;
    __int64 rpg[14];
    rpg[0] = 0;
    rpg[1] = 0;
    rpg[2] = 1;
    for(__int64 i = 3; i < 14; i++)
        rpg[i] = (i-1) * (rpg[i - 1] + rpg[i - 2]);
    while(cin >> n)
    {
        if(n == 0)
            break;
        way = 1;
        m = n/2;
        for(__int64 i = 2; i <= m; i++)
            way = way + fun(n,i) * rpg[i];
        cout << way << endl;
    }
    return 0;
}

你可能感兴趣的:(错排)