[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 }

 

posted @ 2017-10-30 20:11  skylee03  阅读(132)  评论(0编辑  收藏  举报