错排

问题: 十本不同的书放在书架上。现重新摆放,使每本书都不在原来放的位置。有几种摆法?
这个问题推广一下,就是错排问题,是组合数学中的问题之一。考虑一个有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("\n");
    }*/
}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

 

posted @ 2017-09-19 15:03  安月冷  阅读(376)  评论(0编辑  收藏  举报