错排
问题: 十本不同的书放在书架上。现重新摆放,使每本书都不在原来放的位置。有几种摆法?
这个问题推广一下,就是错排问题,是组合数学中的问题之一。考虑一个有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; }
2.bzoj4517 排列计数
https://www.cnblogs.com/L-Memory/p/9917967.html
折花枝,恨花枝,准拟花开人共卮,开时人去时。
怕相思,已相思,轮到相思没处辞,眉间露一丝。