• 错排


    考虑一个有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;
    }

     

  • 相关阅读:
    IceMx.Mvc 我的js MVC 框架 二、视图的数据绑定
    IceMx.Mvc 我的js MVC 框架 一、html代码的分离(视图)
    Android Socket编程基础
    第七次作业
    第六次作业
    第五次作业
    第4次作业
    关于个人项目的总结,最主要是不足
    第二次作业
    微信的优缺点以及发展史
  • 原文地址:https://www.cnblogs.com/LinKArftc/p/4902682.html
Copyright © 2020-2023  润新知