错位排列及有关例题

错位排列

对于n的一个排列\(a_{1}, a_{2}, a_{3} ... a_{n}\)。如果所有的\(a_{i}\) 不等于 \(i\),那么这个排列称为错位排列

求解方法

考虑递推

前$i$个元素时如何进行状态转移?

(一)根据错位排列的定义,第$i$个元素肯定不会放在自己的位置上,故第$i$个元素的位置有$i-1$种选择。

(二)对于剩下的$i-1$个元素,选择其中任意一个元素$k$。此时$k$的位置有两种选择:

1. 放在第$i$个元素的位置上,宏观上相当于$i$与$k$的位置互换了。而剩下的i-2个元素依然要求错位排列。方案数为$f[i-2]$

2. 不放在第$i$个元素的位置上,则相当于剩下的$i-1$个元素全部进行错位排列。方案数为$f[i-1]$

综上,我们可以得到$$f[i] = (i-1) * ( f[i-1] + f[i-2] )$$


 

例题 选新娘

题目大意:一共有$N$对新婚夫妇,其中有$M$个新郎找错了新娘,求发生这种情况一共有多少种可能.

有$M$个新郎找错了,意味着有且只有$M$个人是错排,另外人都刚好恰合。于是先求出$f[M]$,表示长度为$M$的序列的错排。

然而这样就完了吗?并没有,还要看看是哪$M$个人。从$N$个人中选$M$个,也就是组合数\(C_N^M\),于是答案就是\(C_N^M * f[M]\)

/*By QiXingzhi*/
#include <cstdio>
#define  r  read()
#define  Max(a,b)  (((a)>(b)) ? (a) : (b))
#define  Min(a,b)  (((a)<(b)) ? (a) : (b))
using namespace std;
typedef long long ll;
#define int ll
const int N = 100010;
const int M = 1010;
const int INF = 1061109567;
inline int read(){
    int x = 0; int w = 1; register int c = getchar();
    while(c ^ '-' && (c < '0' || c > '9')) c = getchar();
    if(c == '-') w = -1, c = getchar();
    while(c >= '0' && c <= '9') x = (x << 3) +(x << 1) + c - '0', c = getchar();
    return x * w;
}
int n,m;
int f[30];
inline int JieCheng(int x){
    int res = 1;
    for(int i = 2; i <= x; ++i) res *= i;
    return res;
}
inline int C(int m, int n){
    return JieCheng(n) / (JieCheng(m) * JieCheng(n-m));
}
#undef int
int main(){
#define int ll
    n = r;
    m = r;
    f[1] = 0;
    f[2] = 1;
    for(int i = 3; i <= m; ++i){
        f[i] = (i - 1) * (f[i-2] + f[i-1]);
    }
    printf("%lld",f[m] * C(m,n));
    return 0;
}

 

posted @ 2018-07-09 20:16  DennyQi  阅读(1285)  评论(0编辑  收藏  举报