1. 程式人生 > >洛谷 P2473 [SCOI2008]獎勵關 解題報告

洛谷 P2473 [SCOI2008]獎勵關 解題報告

有時 init line ++ 簡單 情況 %d 種類 整數

P2473 [SCOI2008]獎勵關

題目描述

你正在玩你最喜歡的電子遊戲,並且剛剛進入一個獎勵關。在這個獎勵關裏,系統將依次隨機拋出\(k\)次寶物,每次你都可以選擇吃或者不吃(必須在拋出下一個寶物之前做出選擇,且現在決定不吃的寶物以後也不能再吃)。

寶物一共有\(n\)種,系統每次拋出這\(n\)種寶物的概率都相同且相互獨立。也就是說,即使前\(k-1\)次系統都拋出寶物1(這種情況是有可能出現的,盡管概率非常小),第\(k\)次拋出各個寶物的概率依然均為\(1/n\)

獲取第\(i\)種寶物將得到\(P_i\)分,但並不是每種寶物都是可以隨意獲取的。第i種寶物有一個前提寶物集合\(S_i\)

。只有當\(S_i\)中所有寶物都至少吃過一次,才能吃第\(i\)種寶物(如果系統拋出了一個目前不能吃的寶物,相當於白白的損失了一次機會)。註意,\(P_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]獎勵關 解題報告