洛谷 P2473 [SCOI2008]獎勵關 解題報告
P2473 [SCOI2008]獎勵關
題目描述
你正在玩你最喜歡的電子遊戲,並且剛剛進入一個獎勵關。在這個獎勵關裏,系統將依次隨機拋出\(k\)次寶物,每次你都可以選擇吃或者不吃(必須在拋出下一個寶物之前做出選擇,且現在決定不吃的寶物以後也不能再吃)。
寶物一共有\(n\)種,系統每次拋出這\(n\)種寶物的概率都相同且相互獨立。也就是說,即使前\(k-1\)次系統都拋出寶物1(這種情況是有可能出現的,盡管概率非常小),第\(k\)次拋出各個寶物的概率依然均為\(1/n\)。
獲取第\(i\)種寶物將得到\(P_i\)分,但並不是每種寶物都是可以隨意獲取的。第i種寶物有一個前提寶物集合\(S_i\)
假設你采取最優策略,平均情況你一共能在獎勵關得到多少分值?
輸入輸出格式
輸入格式:
第一行為兩個正整數\(k\)和\(n\),即寶物的數量和種類。以下\(n\)行分別描述一種
寶物,其中第一個整數代表分值,隨後的整數依次代表該寶物的各個前提寶物(各寶物編號為1到\(n\)),以0結尾。
輸出格式:
輸出一個實數,保留六位小數,即在最優策略下平均情況的得分。
說明
\(1<=k<=100, 1<=n<=15\),分值為\([-10^6,10^6]\)內的整數。
想做這個題得先弄懂條件概率
簡單一點的解釋是,B在A發生的條件下發生的概率。
舉個栗子,擲色子第一次投6概率為1/6,為A事件,第二次投6概率仍為1/6,為B事件。如果把兩次投擲產生的一個結果算成一個最終狀態,那麽連續的狀態AB發生的概率為1/36,也即是B在A發生的條件下發生的概率。
條件概率一定得把連續的事件劃為一個狀態來求解。
對於具體題目來看,在第\(i\)次出現寶物的時候,我們產生的狀態空間的大小即為\(1/n^i\)。對於其中每一個狀態空間的延長我們都可以做出選和不選的決策(當然,有時候是強制不能選的),以保證最優策略。
當然,即使沒有決策,我們也不能找到所有狀態空間進行統計,我們發現,第\(i\)個階段產生的某一個狀態空間對第\(i+1\)個階段的每一個可能發生的寶物都能產生一個遞推,這可能出現的\(n\)個寶物將狀態空間擴大了\(n\)倍。
於是我們實際上在統計的時候,對於第\(i\)個階段寶物產生的狀態空間,它在後面重復出現了\(n^{k-i}\)次,所以這一維所有的答案產生的貢獻最後需要除上\(n^i\),我們通過倒推來消除可能爆精度的問題(在後面具體提到)
如果進行決策,我們利用背包的思想,將狀態空間用一個新的狀態表示處理,這也是轉移方程中狀態壓縮的一維\(j\),\(j\)表示當前狀態空間每個寶物是否出現。註意新的狀態空間可能代表多個以往的狀態空間。
按照順著的思想從前向後遞推,我們用新狀態空間對當前階段每一個可能出現的概率進行遞推,等價於原狀態空間對每一個概率進行遞推。這時候會產生兩個問題,一是我們對每一個狀態空間都得樸素的除上\(n^i\),會產生新的復雜度。二是我們需要額外的判斷,保證統計答案時的合法性,比較麻煩。
所以我們進行倒著做,可以對每一次產生的新狀態都除以\(n\),而不必對每一個狀態特殊判斷。最後統計答案時也只有唯一的一個合法。
方程:\(dp[i][j]\)代表第\(i\)階段\(j\)狀態已經發生轉移的最大分數。
轉移:\(dp[i][j]+=\sum_{l=1}^n max(dp[i+1][j|(1<<l-1)],dp[i+1][j])\),\(max\)左邊要判轉移合法
目標:\(dp[1][0]\)
可能說得不嚴謹,大概只是個人的一點淺顯的感性理解,今天也是第一次做條件概率的題,如有不足,還請提出。
Code:
#include <cstdio>
const int N=102;
double dp[N][1<<15],score[18];
double max(double x,double y){return x>y?x:y;}
int n,k,pre[18];
void init()
{
scanf("%d%d",&k,&n);
int pree;
for(int i=1;i<=n;i++)
{
scanf("%lf%d",score+i,&pree);
while(pree)
{
pre[i]|=1<<pree-1;
scanf("%d",&pree);
}
}
}
void work()
{
for(int i=k;i;i--)
for(int j=0;j<=1<<n;j++)
{
for(int l=1;l<=n;l++)
{
if((pre[l]&j)==pre[l])
dp[i][j]+=max(dp[i+1][j|(1<<l-1)]+score[l],dp[i+1][j]);
else
dp[i][j]+=dp[i+1][j];
}
dp[i][j]/=double(n);
}
printf("%.6lf",dp[1][0]);
}
int main()
{
init();
work();
return 0;
}
2018.7.3
洛谷 P2473 [SCOI2008]獎勵關 解題報告