PAT 发邮件 (错排问题) - 详细题解

这是一个非常经典的数学问题, 错排问题

组合学中有这样一个问题:某人给五个朋友写信,邀请他们来家中聚会。请柬和信封交由助手去处理。粗心的助手却把请柬全装错了信封。请问:助手会有多少种装错的可能呢?
这个问题是是由当时有名的数学家约翰·伯努利(Johann Bernoulli,1667—1748)的儿子丹尼尔·伯努利(Danid Bernoulli,17OO一1782)提出来的。瑞士著名数学家欧拉按一般情况给出了一个递推公式:
    用A、B、C……表示写着n位友人名字的信封,a、b、c……表示n份相应的写好的信纸。把错装的总数为记作D(n)。假设把a错装进B里了,包含着这个错误的一切错装法分两类:
(1)b装入A里,这时每种错装的其余部分都与A、B、a、b无关,应有D(n-2)种错装法。    
(2)b装入A、B之外的一个信封,这时的装信工作实际是把(除a之外的)n-1份信纸b、c……装入(除B以外的)n-1个信封A、C……,显然这时装错的方法有D(n-1)种。
总之在a装入B的错误之下,共有错装法D(n-2)+D(n-1)种。
a装入C,装入D……的n-2种错误之下,同样都有D(n-1)+D(n-2)种错装法,因此D(n)=(n-1)[D(n-1)+D(n-2)]
  这是递推公式,令n=1、2、3、4、5逐个推算就能解答蒙摩的问题。
    D(1)=0,D(2)=1,D(3)=2,D(4)=9,D(5)=44
答案是44种。

记着公式会用就好了, 有兴趣的同学可以推演一下, 不算太难理解

//发邮件 错排问题
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <vector>
#include <queue>
#include <cmath>
using namespace std;
#define ms(x, n) memset(x,n,sizeof(x));
typedef  long long LL;
const LL maxn = 25;

LL cont[maxn]; //cont[i]表示把i个邮件全部装错的方案数
void init()
{
    cont[1] = 0, cont[2] = 1, cont[3] = 2;
    for(int i = 4; i <= maxn; i++)
        cont[i] = (i-1)*(cont[i-1]+cont[i-2]);
}
int main()
{
    int n;
    init();
    while(cin >> n){
        cout << cont[n] << endl;
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/a1097304791/article/details/84197731