bzoj1076 獎勵關 期望dp
題目大意:總共有k次彈出寶物的機會,寶物共有n種,彈出不同的寶物的概率相同的,是每個寶物都有價值,和選擇這個寶物的限制(必須具有特定的寶物),問最後的最優期望是多少。
思路:“正向推概率,反向推期望。”,一看資料範圍就知道肯定是狀壓。
這裡推薦一個大佬的部落格 https://blog.csdn.net/nameofcsdn/article/details/52082746
考慮f[ i ][ j ],j為二進位制數,表示在第i個格子之前具有了 j 的狀態,那在這個格子,對於每一個物體,有能吃和不能吃兩種情況。(用( j | t)==j 來判斷j是否包含了t)。
對於能吃情況(即滿足限制條件),那我可以選擇吃或者不吃,由於我已經算出了第i+1層的所有期望,所以我只要選擇吃和不吃裡的最大值就可以了,由於這個格子彈出的物品總共有n種情況,所以要記得概率要除以n。
f[i][j]+=max(f[i+1][j],f[i+1][j|(1<<(x-1))]+w[x])/n;//每一個x都對應1/n的情況,每個i,j都有兩個方向,即這個東西吃還是不吃
對於不能吃的情況,只能選擇不吃。
f[i][j]+=f[i+1][j]/n;//只有一個方向
所以這樣dp結束後,f[ 1 ][ 0 ]就是我們要的答案。
為什麼期望要倒著做呢,第一,正著做wa了。。。第二,倒著做保證了dp時全是合法的情況。
#include<bits/stdc++.h> #define CLR(a,b) memset(a,b,sizeof(a)) usingnamespace std; typedef long long ll; double dp[110][50000],f[110][50000],w[20]; int t[20]; int k,n; int main(){ cin>>k>>n; for(int i=1;i<=n;i++) { scanf("%lf",&w[i]); int x; while(scanf("%d",&x),x){ t[i]|=(1<<(x-1)); } }for(int i=k;i>0;i--) { for(int j=0;j<(1<<n);j++) { for (int x=1;x<=n;x++) { if((j|t[x])==j)f[i][j]+=max(f[i+1][j],f[i+1][j|(1<<(x-1))]+w[x])/n;//每一個x都對應1/n的情況,每個i,j都有兩個方向,即這個東西吃還是不吃 else f[i][j]+=f[i+1][j]/n;//只有一個方向 } } } printf("%.6f\n",f[1][0]); }
1076: [SCOI2008]獎勵關
Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 3830 Solved: 2071
[Submit][Status][Discuss]
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 21 0
2 0
Sample Output
1.500000HINT
【資料規模】
1<=k<=100,1<=n<=15,分值為[-10^6,10^6]內的整數。