1. 程式人生 > >[SCOI2008]獎勵關

[SCOI2008]獎勵關

true etc eof dig [0 cnblogs amp sdi code

題目大意:
  你有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 }

[SCOI2008]獎勵關