@LinKArftc
2015-10-22T11:38:49.000000Z
字数 2272
阅读 2213
数学
排列组合
考虑一个有n个元素的排列,若一个排列中所有的元素都不在自己原来的位置上,那么这样的排列就称为原排列的一个错排。 n个元素的错排数记为D(n)
典型问题如在写信时将n封信装到n个不同的信封里,有多少种全部装错信封的情况?又比如四人各写一张贺年卡互相赠送,有多少种赠送方法?自己写的贺年卡不能送给自己。等等……
在组合数学,错排是指没有元素出现在自己原本位置的排列。即是说存在没有不动点的双射
$:S->S,
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))
个人想法:当k排在第n位时,除了n和k以外还有n-2个数,其错排数为D(n-2)。当k不排在第n位时,假设k排在了第m位,则等价于去除k,n排在了第m位,所以为D(n-2)
至于通项公式的推导,比较复杂,详见:
http://zh.wikipedia.org/wiki/%E9%94%99%E6%8E%92%E9%97%AE%E9%A2%98
//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\n", f[n][m]);
}
return 0;
}
每个盒子放球数 >=0
假设n个盒子分别放x1,x2,x3...xn
,则x1+x2+x3+...+xn=k
,另yi=xi+1(1<=i<=n)
,则yi>=1
且y1+y2+...+yn=n+k
,相当于求yi这个方程的解组数,可以看成插板问题,往n+k
个物品中插入n-1
个板,所以答案是C(n+k-1,n-1)=C(n+k-1,k)
/*
*输入说明:k个相同的球放入n个不同的盒子
*输入每行有两个正整数n和k
/*
int com(int n, int m) {
m = min(m, n - m);
int res = 1;
for (int i = 0, j = 1; i < m; i ++) {
res *= n - i;
while (j <= m && res % j == 0) {
res /= j;
j ++;
}
}
return res;
}
int main() {
int n, k;
while (~scanf("%d %d", &n, &k)) {
printf("%d\n", com(n+k-1, k));
}
return 0;
}
每个盒子放球数>=0
POJ1664
const int maxn = 20;
int dp[maxn][maxn];//i个球放入j个盒子方法数
void init() {
for (int j = 1; j <= 10; j ++) dp[0][j] = dp[1][j] = dp[j][1] = dp[j][0] = 1;
for (int i = 2; i <= 10; i ++) {
for (int j = 2; j <= 10; j ++) {
if (i >= j) dp[i][j] = dp[i-j][j] + dp[i][j-1];
else dp[i][j] = dp[i][i];
}
}
}
int main() {
init();
int T, m, n;
scanf("%d", &T);
while (T --) {
scanf("%d %d", &m, &n);
printf("%d\n", dp[m][n]);
}
return 0;
}