Hlg 1755 【组合数学】.cpp

题意:

  问n封信中前m封信全都不是自己应该得到的情况数..

思路:

  3种方法:

  ①. 记忆化搜索..

     比如有n个,k个错排的话。总排列数数n!

     然后这n!个要减掉不符合题意的

      就是k个中1个没有错排,2个没有错排,....k个没有错排
      如果k个中1个没有错排的话,就是 C[k][1]*(n-1个中k-1个错排)
      f[i]=(i-1)*(f[i-1]+f[i-2])

     ②. DP..

    如果用dp[i][j]表示i个人中前j个人是错排的..

      那可以看成i个人中前j个人中某一个人的错排是由4种情况得到的..1st:前j个人中有1个人跟他错排了..而且正好是互换位置了..那就是dp[i-2][j-2]

                                   2ed: 前j个人中1个人跟他错排了..但是不是互换位置的..那就是dp[i-1][j-1]

                                   3rd:后i-j个人中有一个和他错排了..而且正好互换位置了..那就是dp[i-2][j-1]

                                     4th:后i-j个人中有一个人和他错排了..但是不是互换位置..那就是dp[i-1][j]

      所以递推公式是:dp[i][j] = (j-1)(dp[i-2][j-2]+dp[i-2][j-1])+(i-j)(dp[i-2][j-1]+dp[i-1][j])

        所以只有确定一些基础状态就可以推出后面的状态了..

    ③. 用组合数学..

      在后面的n-m个人中找i(0~n-m)个人与前面的m个人进行错排..所以就是(m+i)个人错排的数量*(n-m个人中选i个人的情况数)

      

Tips:

  错排公式为 f[i] = (i-1)*(f[i-1]+f[i-2])

  组合数的公式可以由前两个公式得来..即:C[i][j] = C[i-1][j]+C[i-1][j-1]

  因为两个数相乘的时候还是有可能大于整型的..所以应该用long long..

Code:

  

第三种方法..
 1 #include <stdio.h>
 2 #include <cstring>
 3 #define LL long long
 4 
 5 const int mod = 1000000007;
 6 LL C[1010][1010], P[1010];
 7 LL f[1010], dp[1010][15];
 8 
 9 void init()
10 {
11     memset(dp, 0, sizeof(dp));
12     C[0][0] = 1;
13     for (int i = 1; i < 1010; ++i) {
14         C[i][0] = C[i][i] = 1;
15         for (int j = 1; j < i; ++j) {
16             C[i][j] = C[i-1][j-1]+C[i-1][j];
17             C[i][j] %= mod;
18         }
19     }
20 
21     P[0] = P[1] = 1;
22     for (int i = 2; i < 1010; ++i) {
23         P[i] = i*P[i-1];
24         P[i] %= mod;
25     }
26 
27     f[0] = f[1] = 0, f[2] = 1;
28     for (int i = 3; i < 1010; ++i) {
29         f[i] = (i-1)*(f[i-1]+f[i-2]);
30         f[i] %= mod;
31     }
32 }
33 
34 int main()
35 {
36     int n, m;
37     init();
38     while (~scanf("%d %d", &n, &m)) {
39         if (dp[n][m] == 0) {
40             for (LL i = 0; i <= n-m; ++i) {
41                 dp[n][m] += C[n-m][i]%mod*f[i+m]%mod;
42                 dp[n][m] %= mod;
43             }
44         }
45         printf("%lld\n", dp[n][m]%mod);
46     }
47     return 0;
48 }

链接:http://acm.hrbust.edu.cn/index.php?m=ProblemSet&a=showProblem&problem_id=1755

原文地址:https://www.cnblogs.com/Griselda/p/3051747.html