zoukankan      html  css  js  c++  java
  • 【BZOJ-1426】收集邮票 概率与期望DP

    1426: 收集邮票

    Time Limit: 1 Sec  Memory Limit: 162 MB
    Submit: 261  Solved: 209
    [Submit][Status][Discuss]

    Description

    有n种不同的邮票,皮皮想收集所有种类的邮票。唯一的收集方法是到同学凡凡那里购买,每次只能买一张,并且买到的邮票究竟是n种邮票中的哪一种是等概率的,概率均为1/n。但是由于凡凡也很喜欢邮票,所以皮皮购买第k张邮票需要支付k元钱。 现在皮皮手中没有邮票,皮皮想知道自己得到所有种类的邮票需要花费的钱数目的期望。

    Input

    一行,一个数字N N<=10000

    Output

    要付出多少钱. 保留二位小数

    Sample Input

    3

    Sample Output

    21.25

    HINT

    Source

    Solution

    第一次见概率题这么做的...好厉害

    首先我们定义$g[i]$表示现在有$i$张,要买到$n$张的期望次数;

    定义$P(x,i)$为买$x$次能从$i$种买到$n$种的概率。

    那么可以得到:

    $$g[i]=sum _{x=0}^{infty }x*P(x,i)$$

    那么就有:

    $$g[i]=g[i]*frac{i}{n}+g[i+1]*frac{n-i}{n}+1$$

    $$g[i]-g[i]*frac{i}{n}=g[i+1]*frac{n-i}{n}+1$$

    $$g[i]*frac{n-i}{n}=g[i+1]*frac{n-i}{n}+1$$

    $$g[i]=(g[i+1]*frac{n-i}{n}+1)*frac{n}{n-i}$$

    得到$g[i]=g[i+1]+frac{n}{n-i}$ ,且知道$g[n]=0$

    那么我们设$f[i][j]$表示还现在有$i$张,下一张是$j$元,买到$n$张的期望

    显然$f[i][j]$到$f[i][j+1]$的概率是$frac{i}{n}$,到$f[i+1][j+1]$的概率是$frac{n-i}{n}$,并且付出的代价都是$j$

    所以转移显然:

    $$f[i][j]=frac{i}{n}*f[i][j+1]+frac{n-i}{n}*f[i+1][j+1]+j$$

    但是$f[i][j]$是的递推是无穷大的,所以不能直接递推,考虑它的一些性质:

    $$f[i][j]=sum_{x=0}^{infty }[j+(j+1)+...+(x+j-1)]*P(x,i)$$

    显然是个等差数列求和,所以可以得到:

    $$f[i][j]=sum _{x=0}^{infty }frac{x*[(j)+(x+j-1)]}{2}*P(x,i)$$

    然后我们作差$f[i][j+1]-f[i][j]$得到:

    $$f[i][j+1]-f[i][j]=sum_{x=0}^{infty}x*P(x,i)  Leftrightarrow  f[i][j+1]-f[i][j]=g[i]$$

    所以我们就可以对开始时$f[i][j]$这个式子进行化简,得到:

    $$f[i][j]=f[i][j+1]*frac{i}{n}+f[i+1][j+1]*frac{n-i}{n}$$

    $$Rightarrow f[i][j]=(f[i][j]+g[i])*frac{i}{n}+(f[i+1][j]+g[i+1])*frac{n-i}{n}+j$$

    $$f[i][j]=frac{[(f[i+1][j]+g[i+1])*frac{n-i}{n}+g[i]*frac{i}{n}+j]*n}{n-i}$$

    然后我们发现$j$这一维其实是无效的,我们只需要知道$j=1$时的答案,所以我们在转移的时候忽略它,直接令$j=1$,并用$f[i]$表示$f[i][1]$,得到:

    $$f[i]=f[i+1]+g[i+1]+g[i]*frac{i}{n-i}+frac{n}{n-i}$$

    然后我们就可以线性时间得到答案了。

    Code

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cmath>
    #include<cstring>
    using namespace std;
    #define MAXN 10010 
    int N;
    double g[MAXN],f[MAXN];
    int main()
    {
        scanf("%d",&N);
        for (int i=N-1; i>=0; i--) g[i]=g[i+1]+1.0*N/(N-i);
        for (int i=N-1; i>=0; i--) f[i]=f[i+1]+g[i+1]+g[i]*1.0*i/(N-i)+1.0*N/(N-i); 
        printf("%.2lf
    ",f[0]); 
    }
    

      

    码量比思路量不知道小到哪去了!!

  • 相关阅读:
    代码整洁之道它山之玉可以重构:身份证号码解析、验证工具(第一天)
    这也是C#代码吗 代码阅读性进阶:测试文档化
    它山之玉可以重构:身份证号码解析、验证工具(第三天)
    它山之玉可以重构:身份证号4(第四天)
    自己动手写压力测试三剑客HttpClient+Async+Parallel
    三层架构,够不够DDD眼中的三层(附C#源代码实现)
    百般武艺为哪般再谈业务域的核心地位
    深入浅出裸测之道单元测试的单元化
    它山之玉可以重构:身份证号码解析、验证工具(第二天)
    重构之重与敏捷之轻身份证号重构回顾
  • 原文地址:https://www.cnblogs.com/DaD3zZ-Beyonder/p/5904217.html
Copyright © 2011-2022 走看看