1. 程式人生 > >CF398B Painting The Wall 概率期望

CF398B Painting The Wall 概率期望

思想 遞推 理解 pre bit main $1 open void

題意:有一個 $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\n", 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

CF398B Painting The Wall 概率期望