【SCOI2008】奖励关 题解(状压DP+期望)
题目大意:给定$n$个宝物,每次随机抛出一个宝物,奖励分数为$p_i$。但如果选这个宝物必须选过它的前置宝物集合。共进行$K$轮问最优策略下的期望。
$n\leq 15,-10^6\leq p_i\leq 10^6$
--------------------------
看到数据范围,状压很容易想到。
设$f[i][j]$表示到了第$i$轮,宝物取舍状态为$j$的最大期望得分。
但这样表示有一个问题:可能在第$i$轮没法到达状态$j$。我们无法预知未来。
改一下定义:$f[i][j]$表示第$1$轮到第$i-1$轮宝物取舍状态为$j$,第$i$轮到第$K$轮的最大期望得分。有如下状态转移方程:
如果前置宝物都已经取过,那么有$f[i][j]+=\max(f[i+1][j],f[i+1][j|(1<<(k-1))]+p[k])$
如果没有,则直接继承先前状态:$f[i][j]+=f[i+1][j]$。
最后再除$n$即为期望值。
代码:
#include<bits/stdc++.h> using namespace std; int n,K,p[16],s[16],x; double f[105][1<<16]; inline int read() { int x=0,f=1;char ch=getchar(); while(!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();} while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();} return x*f; } int main() { K=read(),n=read(); for (int i=1;i<=n;i++) { p[i]=read(); while(x=read()) s[i]=s[i]|(1<<x-1); } for (int i=K;i>=1;i--) for (int j=0;j<(1<<n);j++) { for (int k=1;k<=n;k++) if ((j&s[k])==s[k]) f[i][j]+=max(f[i+1][j],f[i+1][j|(1<<k-1)]+p[k]); else f[i][j]+=f[i+1][j]; f[i][j]/=n; } printf("%lf",f[1][0]); return 0; }