错排

考虑一个有n个元素的排列,若一个排列中所有的元素都不在自己原来的位置上,那么这样的排列就称为原排列的一个错排。 n个元素的错排数记为D(n)

典型问题如在写信时将n封信装到n个不同的信封里,有多少种全部装错信封的情况?又比如四人各写一张贺年卡互相赠送,有多少种赠送方法?自己写的贺年卡不能送给自己。等等……

定义

在组合数学,错排是指没有元素出现在自己原本位置的排列。即是说存在没有不动点的双射 
$:S->S, (i)!=i,1<=i<=n(n)的值如下:(由n=1起:) 
0, 1, 2, 9, 44, 265, 1854, 14833, 133496, 1334961, 14684570, 176214841, 2290792932, ... 
例如有n封写好了的信,收件人不同,胡乱放入n个写了地址的信封中,寄出,求没有一个收件人收到他所应接收的信的概率。当n=4,在4! = 24个排列之中,只有9个是错排: 
BADC, BCDA, BDAC, 
CADB, CDAB, CDBA, 
DABC, DCAB, DCBA, 
所以有关概率为9/24 = 37.5%

递推公式

显然D(1)=0,D(2)=1。当n ≥ 3时,不妨设n排在了第k位,其中k≠n,也就是1 ≤ k ≤ n-1 。 那么我们现在考虑第n位的情况。 
当k排在第n位时,除了n和k以外还有n-2个数,其错排数为D(n-2)。 
当k不排在第n位时,那么将第n位重新考虑成一个新的“第k位”,这时的包括k在内的剩下n-1个数的每一种错排,都等价于只有n-1个数时的错排(只是其中的第k位会换成第n位)。其错排数为D(n-1)。 
所以当n排在第k位时共有D(n-2)+D(n-1)种错排方法,又k有从1到n-1共n-1种取法,我们可以得到: 
D(n)=(n-1)(D(n-1)+D(n-2))

至于通项公式的推导,比较复杂,详见: 
http://zh.wikipedia.org/wiki/%E9%94%99%E6%8E%92%E9%97%AE%E9%A2%98

个人想法:当k排在第n位时,除了n和k以外还有n-2个数,其错排数为D(n-2)。当k不排在第n位时,假设k排在了第m位,则等价于去除k,n排在了第m位,所以为D(n-2)

const int p = 1000000007;
long long d[110];
d[1] = 0;
d[2] = 1;
for (int i = 3; i <= 100; i ++) d[i] = ((d[i-1] + d[i-2]) % p) * (i-1) % p;

错排概率

求错排概率可以用 dp 的方法:例 zoj 1619

//f[n][m] = f[n-m][0] / m!

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;

double f[110][110];

int main() {
//    freopen("input.txt", "r", stdin);
    f[1][0] = 0;
    f[1][1] = 1;
    f[0][1] = 0;
    f[0][0] = 1;
    double sum, tp;
    for (int i = 2; i <= 100; i ++) {
        sum = 0.0;
        for (int j = 1; j <= i; j ++) {
            tp = f[i-j][0];
            for (int k = 2; k <= j; k ++) tp /= k;
            f[i][j] = tp;
            sum += tp;
        }
        f[i][0] = 1 - sum;
    }

    int n, m;
    while (~scanf("%d %d", &n, &m)) {
        printf("%.8f
", f[n][m]);
    }
    
    return 0;
}

HDU1465 不容易系列之一

const int maxn = 30;
ll dp[maxn];

void init() {
    dp[1] = 0;
    dp[2] = 1;
    dp[3] = 2;
    for (int i = 4; i <= 20; i ++) {
        dp[i] = (i - 1) * (dp[i-1] + dp[i-2]);
    }
}

int main() {

    init();
    int n;
    while (~scanf("%d", &n)) printf("%lld
", dp[n]);

    return 0;
}

 

原文地址:https://www.cnblogs.com/LinKArftc/p/4902682.html