codeforces--698C LRU狀壓+概率DP
阿新 • • 發佈:2020-07-04
題目連結:https://codeforces.com/problemset/problem/698/C
題目大意:有n種節目,每個節目出現的概率為$p_i$,電視的快取大小為V,當快取滿了後會擠掉出現次數最少的節目,問在$10^{100}$次詢問後每個節目存在快取中的概率。
Examples
Input3 1Output
0.3 0.2 0.5
0.3 0.2 0.5Input
2 1Output
0.0 1.0
0.0 1.0Input
3 2Output
0.3 0.2 0.5
0.675 0.4857142857142857 0.8392857142857143Input
3 3Output
0.2 0.3 0.5
1.0 1.0 1.0
由於n非常小,所以我們可以直接用二進位制列舉快取中節目的狀態,假設dp[sta]表示快取中儲存狀態為sta的概率,那麼其中第i個1的存在表示第i個節目存在快取中,我們只需要將每個位數為1的地方做過累加就是每位的最終概率了。
由於$10^{100}$非常大,所以當n個節目中存在非0概率的個數大於快取容量時快取一定會爆,而小於等於的時候不會爆,所以每個非零概率的節目都會存在於快取中
所以我們特判一下小於等於的情況,之後就是對爆的情況做狀壓。
我們列舉狀態sta,當sta中1的個數等於快取容量的時候就是最終狀態了,我們就可以做答案處理,如果sta中1的個數小於快取容量,那麼我們就需要進行狀態轉移了。也就是對sta中0的元素進行轉移即:
for (int j=1; j<=n; j++) if ((i&(1<<(j-1)))==0) dp[i|(1<<(j-1))]+=dp[i]*p[j]/zros;//有zros份可以補充,其中j佔據了p[j]份
也就是對該位的0進行補充,但需要注意的是sta有很多個0要補充,所以我們要將這所有的0位置的概率做個累加記為zros,那麼補充該位置的0的概率就是p[j]/zros
那麼答案也就很容易出來了
以下是AC程式碼:
#include <cstdio> #include <cstring> #include <algorithm> usingnamespace std; const double esp=1e-8; #define debug printf("@#$#@$#@%#@1^&^**@#$#@%))#%\n" ) double p[30],dp[1<<21],ans[30]; int main() { int n,v,cnt=0; scanf ("%d%d",&n,&v); for (int i=1; i<=n; i++){ scanf ("%lf",&p[i]); if (p[i]>=esp) cnt++; } if (cnt<=v) { for (int i=1; i<=n; i++) printf("%.6f%c",p[i]<esp?0.0:1.0,i==n?'\n':' '); return 0; } dp[0]=1; for (int i=0; i<(1<<n); i++){ int use=0; double zros=0; for (int j=1; j<=n; j++){ if (i&(1<<(j-1))) use++; else zros+=p[j]; } if (use>v) continue; else if (use==v){ for (int j=1; j<=n; j++) if (i&(1<<(j-1))) ans[j]+=dp[i]; } else { for (int j=1; j<=n; j++) if ((i&(1<<(j-1)))==0) dp[i|(1<<(j-1))]+=dp[i]*p[j]/zros;//有zros份可以補充,其中j佔據了p[j]份 } } for (int i=1; i<=n; i++) printf("%.10f%c",ans[i],i==n?'\n':' '); return 0; }