【BZOJ】1076 [SCOI2008]奖励关 期望DP+状压DP
【题意】n种宝物,k关游戏,每关游戏给出一种宝物,可捡可不捡。每种宝物有一个价值(有负数)。每个宝物有前提宝物列表,必须在前面的关卡取得列表宝物才能捡起这个宝物,求期望收益。k<=100,n<=15。
【算法】期望DP+状压DP
【题解】主要需要记录的状态是前缀已有宝物,所以设f[i][S]表示前i关已有宝物列表S的期望收益。
根据全期望公式,依赖于第i+1关的宝物选择:(如果列表符合)
$$f[i][S]=\sum_{i=1}^{n}\frac{1}{n}*Max(f[i+1][S'],f[i+1][S])\ \ ,\ \ S'=S|(1<<(i-1))$$
倒推是因为已知前缀列表S的情况下,很容易判断下一关宝物是否可捡。
#include<cstdio> #include<algorithm> #include<cstring> using namespace std; const int inf=100000; int a[20],n,m,v[20]; double f[200][70000]; int main() { scanf("%d%d",&n,&m); for(int i=1;i<=m;i++) { scanf("%d",&v[i]); int u;scanf("%d",&u); a[i]=0; while(u!=0) { a[i]|=(1<<(u-1)); scanf("%d",&u); } } int maxe=(1<<m)-1; for(int i=n-1;i>=0;i--) { for(int j=0;j<=maxe;j++) { f[i][j]=0; for(int k=1;k<=m;k++) { if((a[k]&j)==a[k])f[i][j]+=max(f[i+1][j],f[i+1][j|(1<<(k-1))]+v[k]); else f[i][j]+=f[i+1][j]; } f[i][j]/=m; } } printf("%.6lf",f[0][0]); return 0; }