BZOJ1076[SCOI2008] 獎勵關
獎勵關
Description
你正在玩你最喜歡的電子遊戲,並且剛剛進入一個獎勵關。在這個獎勵關裡,系統將依次隨機丟擲k次寶物,每次你都可以選擇吃或者不吃(必須在丟擲下一個寶物之前做出選擇,且現在決定不吃的寶物以後也不能再吃)。寶物一共有n種,系統每次丟擲這n種寶物的概率都相同且相互獨立。也就是說,即使前k-1次系統都丟擲寶物1(這種情況是有可能出現的,儘管概率非常小),第k次丟擲各個寶物的概率依然均為1/n。 獲取第i種寶物將得到Pi分,但並不是每種寶物都是可以隨意獲取的。第i種寶物有一個前提寶物集合Si。只有當Si中所有寶物都至少吃過一次,才能吃第i種寶物(如果系統丟擲了一個目前不能吃的寶物,相當於白白的損失了一次機會)。注意,Pi可以是負數,但如果它是很多高分寶物的前提,損失短期利益而吃掉這個負分寶物將獲得更大的長期利益。 假設你採取最優策略,平均情況你一共能在獎勵關得到多少分值?
Input
第一行為兩個正整數k和n,即寶物的數量和種類。以下n行分別描述一種寶物,其中第一個整數代表分值,隨後的整數依次代表該寶物的各個前提寶物(各寶物編號為1到n),以0結尾。
Output
輸出一個實數,保留六位小數,即在最優策略下平均情況的得分。
Sample Input
1 2
1 0
2 0
Sample Output
1.500000
HINT
【資料規模】
1<=k<=100,1<=n<=15,分值為[-106,106]內的整數。
題解
概率與期望第一題祭。。。
這道題很容易想到狀壓dp,因為寶物只有15種,我們可以用一個數的二進位制來表示是否有某種寶物。所以可以用dp[i][j]來表示在第i個寶物掉落,寶物拾取情況為j時的期望最大得分。
而這道題因為寶物有前提限制,在順推時不方便判斷當前狀態是否有效,所以我們採用倒推,從有效狀態轉移到有效狀態就方便的多。
於是我們可以這麼轉移狀態:
1.當我們擁有當前寶物的所有前提寶物時,我們可以從吃和不吃兩個狀態轉移過來:
2.反之,只能不吃
這裡巧妙的使用了位運算,當我們判斷該寶物的前提寶物是否吃到時,可以這麼判斷:
這表示寶物a的前提寶物是一的地方,狀態j中也都是一。
還有強制賦值時使用的,p[a]中是,根據的性質,只要兩個數有一個是一,其結果就為一,於是我們便將第a位強行賦成了一。
另外要弄清楚期望是個什麼東西,期望可以看作是所有可能事件的概率乘以時間的權值相加。
在本題中,每種寶物出現的機率,即在本輪中每個事件的發生機率相同,都是,因此每次狀態轉移時我們也要將本次的總分除以n,於是每個狀態的期望得分就是
最後的答案就在初始情況dp[1][0]中。
程式碼
#include<bits/stdc++.h>
using namespace std;
double dp[105][66000];
int n,k,p[20],con[20],val[20];
void in()
{
p[1]=1;
for(int i=2;i<=16;++i)
p[i]=p[i-1]<<1;
int x;
scanf("%d%d",&k,&n);
for(int i=1;i<=n;++i)
{
scanf("%d",&val[i]);
while(scanf("%d",&x)&&x)
con[i]+=p[x];
}
}
void ac()
{
for(int i=k;i>=0;--i)
for(int j=0;j<=p[n+1]-1;++j)
{
for(int a=1;a<=n;++a)
{
if((j&con[a])==con[a])
dp[i][j]+=max(dp[i+1][j],dp[i+1][j|p[a]]+val[a]);
else dp[i][j]+=dp[i+1][j];
}
dp[i][j]/=n;
}
printf("%.6lf",dp[1][0]);
}
int main()
{
in();ac();
return 0;
}