• 错排


    问题: 十本不同的书放在书架上。现重新摆放,使每本书都不在原来放的位置。有几种摆法?
    这个问题推广一下,就是错排问题,是组合数学中的问题之一。考虑一个有n个元素的排列,若一个排列中所有的元素都不在自己原来的位置上,那么这样的排列就称为原排列的一个错排。 n个元素的错排数记为D(n)。 研究一个排列错排个数的问题,叫做错排问题或称为更列问题。
     
    当n个编号元素放在n个编号位置,元素编号与位置编号各不对应的方法数用D(n)表示,那么D(n-1)就表示n-1个编号元素放在n-1个编号位置,各不对应的方法数,其它类推.
    第一步,把第n个元素放在一个位置,比如位置k,一共有n-1种方法;
    第二步,放编号为k的元素,这时有两种情况:⑴把它放到位置n,那么,对于剩下的n-1个元素,由于第k个元素放到了位置n,剩下n-2个元素就有D(n-2)种方法;⑵第k个元素不把它放到位置n,这时,对于这n-1个元素,有D(n-1)种方法;
    综上得到
    D(n) = (n-1) [D(n-2) + D(n-1)]
    特殊地,D(1) = 0, D(2) = 1.
    下面通过这个递推关系推导通项公式
    为方便起见,设D(k) = k! N(k), k = 1, 2, …, n,
    则N(1) = 0, N(2) = 1/2.
    n ≥ 3时,n! N(n) = (n-1) (n-1)! N(n-1) + (n-1)! N(n-2)
    即 nN(n) = (n-1) N(n-1) + N(n-2)
    于是有N(n) - N(n-1) = - [N(n-1) - N(n-2)] / n = (-1/n) [-1/(n-1)] [-1/(n-2)]…(-1/3) [N(2) - N(1)] = (-1)^n / n!.
    因此
    N(n-1) - N(n-2) = (-1)^(n-1) / (n-1)!,
    N(2) - N(1) = (-1)^2 / 2!.
    相加,可得
    N(n) = (-1)^2/2! + … + (-1)^(n-1) / (n-1)! + (-1)^n/n!
    因此
    D(n) = n! [(-1)^2/2! + … + (-1)^(n-1)/(n-1)! + (-1)^n/n!].
     
     
    用容斥原理也可以推出错排公式:
    正整数1, 2, 3, ……, n的全排列有 n! 种,其中第k位是k的排列有 (n-1)! 种;当k分别取1, 2, 3, ……, n时,共有n*(n-1)!种排列是至少放对了一个的,由于所求的是错排的种数,所以应当减去这些排列;但是此时把同时有两个数不错排的排列多排除了一次,应补上;在补上时,把同时有三个数不错排的排列多补上了一次,应排除;……;继续这一过程,得到错排的排列种数为
    D(n) = n! - n!/1! + n!/2! - n!/3! + … + (-1)^n*n!/n! = ∑(k=2~n) (-1)^k * n! / k!,
    即D(n) = n! [1/0! - 1/1! + 1/2! - 1/3! + 1/4! + ... + (-1)^n/n!].
    其中,∑表示连加符号,k=2~n是连加的范围;0! = 1,可以和1!相消。
     
    最重要的还是递推式
    D(n) = (n-1) [D(n-2) + D(n-1)]
     
    1.bzoj4563放棋子
    给你一个N*N的矩阵,每行有一个障碍,数据保证任意两个障碍不在同一行,任意两个障碍不在同一列,要求你在
    这个矩阵上放N枚棋子(障碍的位置不能放棋子),要求你放N个棋子也满足每行只有一枚棋子,每列只有一枚棋子
    的限制,求有多少种方案。
     
    /*
    每一行每一列只能放1个求方案数 转化为错排问题
    练习高精压位 压9位。。。
    */
    #include <iostream>
    #include <cstdio>
    #include<iomanip>
    
    #define N 2001
    #define mod 1000000000
    #define _ 9
    #define ll long long
    
    using namespace std;
    ll n;
    struct num
    {
        ll d[N],w;
    /*    void print()
        {
            for (ll i=w;i>=1;i--) cout<<d[i];
            printf("
    ");
        }*/
    }D[N],id;
    
    num operator +(num p1,num p2)
    {
        num ret=id;
        ll g=0;
        if (p1.w<p2.w) swap(p1,p2);
        ret.w=p1.w;
        for (ll i=1;i<=p1.w;i++)
        {
            ret.d[i]=(p1.d[i]+p2.d[i]+g)%mod;
            g=(p1.d[i]+p2.d[i]+g)/mod;
        }
        while(g) ret.d[++ret.w]=g%mod , g/=mod;
        return ret;
    }
    
    num mul(num p1,ll p2)
    {
        num ret=id;
        ret.w=p1.w;
        ll g=0;
        for (ll i=1;i<=p1.w;i++)
        {
            ret.d[i]=(p1.d[i]*p2+g)%mod;
            g=(p1.d[i]*p2+g)/mod;
        }
        while(g) ret.d[++ret.w]=g%mod,g/=mod;
        return ret;
    }
    
    ostream& operator << (ostream &os,num x)
    {
        ll i;
        os<<x.d[x.w];
        for(i=x.w-1;i;i--)
            os<<setfill('0')<<setw(_)<<x.d[i];
        return os;
    }
    
    int main()
    {
    //    freopen("firstmeet.in","r",stdin);
    //    freopen("firstmeet.out","w",stdout);
        scanf("%d",&n);
        if (n == 1)
        {
            puts("0");
            return 0;
        }
        D[1]=id;
        D[2].w=D[2].d[1]=1;
        for (ll i=3;i<=n;i++)
          D[i] = mul((D[i-1]+D[i-2]),i-1);
        //D[n].print();
        cout<<D[n];
        return 0;
    }
    Code

     2.bzoj4517 排列计数

    https://www.cnblogs.com/L-Memory/p/9917967.html

    折花枝,恨花枝,准拟花开人共卮,开时人去时。 怕相思,已相思,轮到相思没处辞,眉间露一丝。
  • 相关阅读:
    hdu 1108 最小公倍数
    hdu 1106 排序
    hdu 1097 A hard puzzle
    hdu 1076 An Easy Task
    hdu 1064 Financial Management
    hdu 1061 Rightmost Digit
    hdu 1050 Moving Tables
    hdu 1060 Leftmost Digit
    hdu 1049 Climbing Worm
    hdu1104
  • 原文地址:https://www.cnblogs.com/L-Memory/p/7551919.html
Copyright © 2020-2023  润新知