[SCOI2008]奖励关
题目大意:
你有k次获取宝物的机会,每次会等概率的从1~n中选出一种宝物给你。
每种宝物都有一个依赖s,表示你只有先吃了s中的所有宝物才能吃当前宝物,如果S没吃完,视作放弃吃当前宝物的机会。
每个宝物有一个价值p,求你获取k个宝物以后的期望收益。
思路:
状压DP。
f[i][j]表示吃了i轮后,状态为j的最大收益。
这样转移看起来很方便,但是遇到不合法状态的时候就会很麻烦。
因此考虑把吃宝物的过程到过来,变成吐宝物。宝物i先于s[i]中所有宝物吐。
考虑吐了i轮,在状态j下,吐出宝物k。
如果s[k]都在j里面,那么可以放心地吐。
f[i][j]+=max(f[i-1][j],f[i-1][j|(1<<k)]+p[k]);
如果不在j里面,那么是不合法状态,吐不出来。
f[i][j]+=f[i-1][j];
最终答案即为f[k][0]。
1 #include<cstdio> 2 #include<cctype> 3 #include<cstring> 4 #include<algorithm> 5 inline int getint() { 6 register char ch; 7 register bool neg=false; 8 while(!isdigit(ch=getchar())) if(ch=='-') neg=true;; 9 register int x=ch^'0'; 10 while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0'); 11 return neg?-x:x; 12 } 13 const int K=2,N=15; 14 int p[N],s[N]; 15 double f[K][1<<N]; 16 int main() { 17 int k=getint(),n=getint(); 18 for(register int i=0;i<n;i++) { 19 p[i]=getint(); 20 for(register int x=getint();x;x=getint()) { 21 s[i]|=1<<(x-1); 22 } 23 } 24 for(register int i=1;i<=k;i++) { 25 memset(f[i&1],0,sizeof *f); 26 for(register int j=0;j<(1<<n);j++) { 27 for(register int k=0;k<n;k++) { 28 if((s[k]&j)==s[k]) { 29 f[i&1][j]+=std::max(f[!(i&1)][j],f[!(i&1)][j|(1<<k)]+p[k])/n; 30 } else { 31 f[i&1][j]+=f[!(i&1)][j]/n; 32 } 33 } 34 } 35 } 36 printf("%.6f\n",f[k&1][0]); 37 return 0; 38 }