[关闭]
@LinKArftc 2015-10-22T11:38:49.000000Z 字数 2272 阅读 2213

排列组合

数学 排列组合


考虑一个有n个元素的排列,若一个排列中所有的元素都不在自己原来的位置上,那么这样的排列就称为原排列的一个错排。 n个元素的错排数记为D(n)

典型问题如在写信时将n封信装到n个不同的信封里,有多少种全部装错信封的情况?又比如四人各写一张贺年卡互相赠送,有多少种赠送方法?自己写的贺年卡不能送给自己。等等……

定义

在组合数学,错排是指没有元素出现在自己原本位置的排列。即是说存在没有不动点的双射
$:S->S, (i)!=i,1<=i<=n(n)的值如下:(由n=1起:)
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

  1. //const int p = 1000000007;
  2. //long long d[110];
  3. d[1] = 0;
  4. d[2] = 1;
  5. for (int i = 3; i <= 100; i ++) d[i] = ((d[i-1] + d[i-2]) % p) * (i-1) % p;

错排概率

错排概率可以用 dp 的方法:例 zoj 1619

  1. //f[n][m] = f[n-m][0] / m!
  2. #include <cstdio>
  3. #include <iostream>
  4. #include <cstring>
  5. #include <algorithm>
  6. using namespace std;
  7. double f[110][110];
  8. int main() {
  9. // freopen("input.txt", "r", stdin);
  10. f[1][0] = 0;
  11. f[1][1] = 1;
  12. f[0][1] = 0;
  13. f[0][0] = 1;
  14. double sum, tp;
  15. for (int i = 2; i <= 100; i ++) {
  16. sum = 0.0;
  17. for (int j = 1; j <= i; j ++) {
  18. tp = f[i-j][0];
  19. for (int k = 2; k <= j; k ++) tp /= k;
  20. f[i][j] = tp;
  21. sum += tp;
  22. }
  23. f[i][0] = 1 - sum;
  24. }
  25. int n, m;
  26. while (~scanf("%d %d", &n, &m)) {
  27. printf("%.8f\n", f[n][m]);
  28. }
  29. return 0;
  30. }

盒子放球

k个相同的球放入n个不同的盒子

每个盒子放球数 >=0

思路

假设n个盒子分别放x1,x2,x3...xn,则x1+x2+x3+...+xn=k,另yi=xi+1(1<=i<=n),则yi>=1y1+y2+...+yn=n+k,相当于求yi这个方程的解组数,可以看成插板问题,往n+k个物品中插入n-1个板,所以答案是C(n+k-1,n-1)=C(n+k-1,k)

  1. /*
  2. *输入说明:k个相同的球放入n个不同的盒子
  3. *输入每行有两个正整数n和k
  4. /*
  5. int com(int n, int m) {
  6. m = min(m, n - m);
  7. int res = 1;
  8. for (int i = 0, j = 1; i < m; i ++) {
  9. res *= n - i;
  10. while (j <= m && res % j == 0) {
  11. res /= j;
  12. j ++;
  13. }
  14. }
  15. return res;
  16. }
  17. int main() {
  18. int n, k;
  19. while (~scanf("%d %d", &n, &k)) {
  20. printf("%d\n", com(n+k-1, k));
  21. }
  22. return 0;
  23. }

m个相同的球放入n个相同的盒子

每个盒子放球数>=0
POJ1664

  1. const int maxn = 20;
  2. int dp[maxn][maxn];//i个球放入j个盒子方法数
  3. void init() {
  4. for (int j = 1; j <= 10; j ++) dp[0][j] = dp[1][j] = dp[j][1] = dp[j][0] = 1;
  5. for (int i = 2; i <= 10; i ++) {
  6. for (int j = 2; j <= 10; j ++) {
  7. if (i >= j) dp[i][j] = dp[i-j][j] + dp[i][j-1];
  8. else dp[i][j] = dp[i][i];
  9. }
  10. }
  11. }
  12. int main() {
  13. init();
  14. int T, m, n;
  15. scanf("%d", &T);
  16. while (T --) {
  17. scanf("%d %d", &m, &n);
  18. printf("%d\n", dp[m][n]);
  19. }
  20. return 0;
  21. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注