zoukankan      html  css  js  c++  java
  • CF398B Painting The Wall 概率期望

    题意:有一个 $n * n$ 的网格,其中 $m$ 个格子上涂了色。每次随机选择一个格子涂色,允许重复涂,求让网格每一行每一列都至少有一个格子涂了色的操作次数期望。
    题解:,,这种一般都要倒推才行。
    设$f[i][j]$表示还有$i$行,$j$列未满足的情况下的期望次数。
    因为每次选择都是完全随机,不受其他东西的影响。
    所以对于题中给出的$m$,实际上就是告诉了我们要求什么东西,假设在已经有那$m$个涂色方块的情况下,我们还有$t1$行,$t2$列未满足,那么我们要求的就是$f[t1][t2]$.
    那么我们可以列出转移方程:(一行写不下,分2行写)
    $$f[i][j] = 1 + frac{ij}{n ^ 2} f[i - 1][j - 1] + frac{(n - i)j}{n ^ 2} f[i][j - 1] $$
    $$f[i][j] += frac{i(n - j)}{n ^ 2} f[i - 1][j] + frac{(n - i)(n - j)}{n^2} f[i][j]$$
    $1$是每次选择的代价,后面的就是进入每一种状态的概率,对于任意后继状态,它对当前状态的贡献就是它的期望 * 进入这个状态的概率(全期望公式)

    然后移项化简,对于后面这一堆东西提出一个$frac{1}{n ^ 2}$,然后把$f[i][j]$放到等式左边,这样就只需要在最后面除一次,可以降低一点精度误差?
    $$f[i][j] = frac{n ^ 2 + ijf[i - 1][j - 1] + (n - i)jf[i][j - 1] + i(n - j)f[i - 1][j]}{[n ^ 2 - (n - i) (n - j)]}$$
    然后因为计算的时候,可能会出现为满足行或列的数量为0的情况,这种时候还放在一起计算就不太方便了(需要特判),因此考虑把这些情况单独拿出来看。
    那么因为$f[i][0]$这个状态之受行的影响,所以可以看做一个优惠券收集问题,即:
    $$f[i][0] = sum_{j = 1}^{i} frac{n}{j}$$
    那么由于$$f[i - 1][0] = sum_{j = 1}^{i - 1} frac{n}{j}$$
    可以得到关于$f[i][0]$和$f[i - 1][0]$的一个递推式,即:
    $$f[i][0] = f[i - 1][0] + frac{n}{i}$$
    解释一下那个和式:如果我们现在还有$i$行未满足,共$n$行,那么选一次可以导致一行新的被满足的概率就是$frac{i}{n}$,那么期望就为$frac{n}{i}$.
    这个东西的感性理解大概是:如果一个事件发生的概率是$frac{1}{5}$,那么显然期望$5$天这个事件就会发生,所以期望是概率的倒数。。。
    不过这个也是可以证明的,只只要将计算式列出,做一个错位相减,利用一下极限的思想,最后可以算出期望确实是概率的倒数。

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 #define R register int
     4 #define AC 2200
     5 #define db double
     6 
     7 int n, m, l, r;
     8 double f[AC][AC];
     9 bool zl[AC], zr[AC];//表示第i行or第i列有没有被标记
    10 
    11 inline int read()
    12 {
    13     int x = 0;char c = getchar();
    14     while(c > '9' || c < '0') c = getchar();
    15     while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
    16     return x;
    17 }
    18 
    19 void pre()
    20 {
    21     n = l = r = read(), m = read();
    22     for(R i = 1; i <= m; i ++)
    23     {
    24         int x = read(), y = read();
    25         if(!zl[x]) zl[x] = true, -- l;
    26         if(!zr[y]) zr[y] = true, -- r;
    27     }
    28 }
    29 
    30 double cal(double x, double y){
    31     return x / y;
    32 }
    33 
    34 void work()
    35 {//因为把i or j为0的状态放在下面一起枚举不太方便(要特判),所以在前面单独求
    36     for(R i = 1; i <= n; i ++)//
    37     {
    38         f[i][0] = f[i - 1][0] + (double)n / i;//因为这个时候只有行的影响,所以只需要考虑行,那么就相当于一个购物券收集问题 
    39         f[0][i] = f[0][i - 1] + (double)n / i;
    40     }
    41     for(R i = 1; i <= l; i ++)
    42         for(R j = 1; j <= r; j ++)
    43         {
    44             /*f[i][j] = 1;
    45             f[i][j] += cal(i * j, n * n) * f[i - 1][j - 1];
    46             f[i][j] += cal((n - i) * j, n * n) * f[i][j - 1];
    47             f[i][j] += cal(i * (n - j), n * n) * f[i - 1][j];
    48             f[i][j] /= 1 - cal((n - i) * (n - j), n * n);*/
    49             f[i][j] = n * n;//把除法放在最后以降低精度误差
    50             f[i][j] += i * j * f[i - 1][j - 1];
    51             f[i][j] += (n - i) * j * f[i][j - 1];
    52             f[i][j] += i * (n - j) * f[i - 1][j];
    53             f[i][j] /= n * n - (n - i) * (n - j);
    54         } 
    55     printf("%.10lf
    ", f[l][r]);
    56 }
    57 
    58 int main()
    59 {
    60     freopen("in.in", "r", stdin);
    61     pre();
    62     work();
    63     fclose(stdin);
    64     return 0;
    65 }
    View Code
  • 相关阅读:
    结对第二次作业
    软件工程第四次作业
    软件工程第三次作业
    软件工程第二次作业
    软件工程第一次作业
    我的大学生活
    python实现栈和队列
    《数据结构与STL-第二章 线性表》读书笔记
    .net/c#常用框架/中间件简介(不定时更新)
    逆变与协变
  • 原文地址:https://www.cnblogs.com/ww3113306/p/10192517.html
Copyright © 2011-2022 走看看