PTA印章

一、题目描述

 

 二、解题思路

  这题是一个概率DP题,我们考虑三种情况

  我们用dp[i][j]代表i张印章能凑出j种,设p = 1 / n

  

  显然第一种情况

    当i<j时,dp[i][j]=0,因为不可能凑出j种

  第二种情况

    当j = 1时,此时选出的i张里面都是某一种,概率是(p^i),又因为可能都是n中的同一种,故都需要乘上n,因此概率为(p^(i-1))

  第三种情况

    当不处于上述两种情况时

      如果此时前i - 1张,凑出了j 种印章,但是我现在拿到的和前j种重复了,此时的概率为(j / n)

      如果此时前i - 1张,凑出了j - 1种印章,但是我现在拿到不属于前j - 1种的概率是(n - j + 1) / n

  故可以写出状态转移方程

 

  if(i < j) dp[i][j] = 0;

  else if(j == 1) dp[i][j] = pow(p,i - 1);

  else dp[i][j] = dp[i - 1][j] * (j * 1.0 / n) + dp[i - 1][j - 1] * ((n - j + 1) * 1.0 / n);

三、代码实现

 1 #include "bits/stdc++.h"
 2 #define PII pair<int,int>
 3 #define rep(i,z,n) for(int i = z;i <= n; i++)
 4 #define per(i,n,z) for(int i = n;i >= z; i--)
 5 #define ll long long
 6 #define db double
 7 #define vi vector<int>
 8 #define debug(x) cerr << "!!!" << x << endl;
 9 using namespace std;
10 //从某个串中把某个子串替换成另一个子串
11 string& replace_all(string& src, const string& old_value, const string& new_value) {
12     // 每次重新定位起始位置,防止上轮替换后的字符串形成新的old_value
13     for (string::size_type pos(0); pos != string::npos; pos += new_value.length()) {
14         if ((pos = src.find(old_value, pos)) != string::npos) {
15             src.replace(pos, old_value.length(), new_value);
16         }
17         else break;
18     }
19     return src;
20 }
21 inline ll read()
22 {
23     ll s,r;
24     r = 1;
25     s = 0;
26     char ch = getchar();
27     while(ch < '0' || ch > '9'){
28         if(ch == '-')
29             r = -1;
30         ch = getchar();
31     }
32     while(ch >= '0' && ch <= '9'){
33         s = (s << 1) + (s << 3) + (ch ^ 48);
34         ch = getchar();
35     }
36     return s * r;
37 }
38 inline void write(ll x)
39 {
40     if(x < 0) putchar('-'),x = -x;
41     if(x > 9) write(x / 10);
42     putchar(x % 10 + '0');
43 }
44 double dp[30][30];
45 int main()
46 {
47     int n,m;
48     n = read();
49     m = read();
50     double p = 1.0 / n;
51     for(int i = 1;i <= m;i++){
52         for(int j = 1;j <= n;j++){
53             //如果i<j,说明此时i张印章一定不能凑出j种印章
54             if(i < j) dp[i][j] = 0;
55             //如果j==1,此时选出的i张里面都是某一种,概率是(p^i),又因为可能都是n中的同一种,故都需要乘上n,因此概率为(p^(i-1))
56             else if(j == 1) dp[i][j] = pow(p,i - 1);
57             //如果当前选的和之前选出的j种是同一种,那么此时的概率为(j / n),因为n中里面选出前j种的概率为(j / n)
58             //如果当前选的和之前的不同,说明增加了一种,此时前面选了j - 1种,我们不选到前面j - 1种的概率为(n -(j - 1)) / n = (n - j + 1) / n
59             else dp[i][j] = dp[i - 1][j] * (j * 1.0 / n) + dp[i - 1][j - 1] * ((n - j + 1) * 1.0 / n);
60         }
61     }
62     cout << fixed << setprecision(4) << dp[m][n];
63     return 0;
64 }
posted @ 2022-03-22 18:26  scannerkk  阅读(51)  评论(0)    收藏  举报