標籤:namespace 表示 轉換 space 為什麼 代碼 int 一個 \n
這個題是真的神仙。。。
根本不會做啊qwq
蒟蒻只會抄題解啊qwq
抄完題解感覺內心愧疚,只好寫一寫自己的理解+注釋來發一篇部落格補償一下qwq
希望能把神仙思路傳遞給其他看到這個題不太會做的dalao們。。。。。
--------------------------------------------------------------以上都是廢話------------------------------------------------------------------------------
期望DP和機率DP不一樣,一般都是倒著推的。(dalao的經驗之談)
至於為什麼倒著推,大家可以去參照網上的其他部落格,反正我也不是很懂(太蒻了)。如果有哪位大佬會,可以私信我謝謝qwq。
--------------------------------------------------------------以上還是廢話-------------------------------------------------------------------------------
好的,進入正題。
題目要求的是得到所有郵票需要花費的錢數的數目期望。
我們設一個\(g[i]\)數組來表示當前收集了i種郵票,到完成收集所有種類的郵票還需要的花費,\(f[i]\)數組表示當前收集了i種郵票,到完成收集所有種類的郵票還需要的次數那麼我們考慮怎麼從後往前倒著DP。
我們把狀態抽離出來,每個狀態都表示現在已經有i種郵票,那麼我們可以很顯然地找出相鄰狀態之間的關係:對於第i個狀態,它有\(i/n\)的機率選中已經選擇過的郵票,那麼狀態不變,除此之外,還有\((n-i)/n\)的機率選中沒有選到的郵票,那麼就轉移到了下一個狀態i+1.
因為蒟蒻我太蒻了,原先沒有怎麼做到期望的題,所以我對反向推算還是有些疑惑的,下面我要稍微解釋一下遞推中每次支付錢數的轉移問題,如果大佬們理解就不用看下一段了qwq
現在我們要找狀態轉移方程,考慮\(g[i]\)怎麼從\(g[i+1]\)轉移過來。但是在這之前我們要搞清楚一件事情:原題中說“購買第k張郵票需要支付k元錢(也就是第k輪的時候支付k元錢,而不是第k種郵票支付k元錢)”,為了方便理解,我們設一共會期望一共會有p輪,那麼順著推的時候權值肯定是1到p的數列。但是現在我們倒著推了,所以我們現在把它轉換成距離達成目標還有k步的時候支付k元錢(也就是正著看的話是p到1的數列),其實這在本質上是一樣的。至於為什麼能想到要開\(f\)數組,其實就是要記錄步數,來獲得該回合需要支付的錢數。
\(f\)數組的轉移比較簡單:
\[f[i]=f[i+1]\times \frac{n-i}{n}+f[i]\times \frac{i}{n}+1\]
就是前面提到的轉移,然後因為原先拿了i+1種郵票,現在只有i種郵票,所以就是又進行了一步,所以肯定是要加上一步的次數(最後+1)
\(g\)數組有點神仙:
\[g[i]=g[i]\times \frac{i}{n}+g[i+1]\times \frac{n-i}{n}+\frac{i}{n}\times (f[i+1]+1)+\frac{n-i}{n}\times (f[i+1]+1)\]
前面的轉移還是和上面的思路一樣,後面的是要加上轉移到自己+轉移到下一個狀態的和,+1的解釋還是和上面一樣qwq
下面是代碼:(注意精度問題,可以開long double比較保險)
#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>#include<cmath>#define MAXN 10010using namespace std;int n;long double f[MAXN],g[MAXN];int main(){ scanf("%d",&n); for(int i=n-1;i>=0;i--) f[i]=f[i+1]+(long double)n/(n-i); for(int i=n-1;i>=0;i--) g[i]=((long double)i/(n-i))*f[i]+g[i+1]+f[i+1]+((long double)n/(n-i)); printf("%.2lf\n",(double)g[0]); return 0;}
收集郵票(數學期望)