1. 程式人生 > >bzoj1076 獎勵關 期望dp

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))
using
namespace 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 MB
Submit: 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 2
1 0
2 0

Sample Output

1.500000

HINT

 

【資料規模】

1<=k<=100,1<=n<=15,分值為[-10^6,10^6]內的整數。