[SCOI2008]奖励关
XXXIV.[SCOI2008]奖励关
\(n\leq 15\)就是一眼状压。但这题难点不是状压,而是期望。
应该很容易就能想到,设\(f[i][j]\)表示前\(i\)次操作后,状态为\(j\)的期望收益。但这有个问题——我们不知道如果刷到一个负数收益应不应该选,因为我们不知道这个负数收益在后面会带给我们怎样的期望收益。
因为必须要直到后面的内容,所以考虑倒序DP:设\(f[i][j]\)表示前\(i\)次操作后状态为\(j\),在后\(K-i\)次操作中的期望收益。这样期望就可以直接取\(\max\)了——对后面的影响已经确定。
对于\(f[i][j]\),我们枚举一个\(k\),表示刷到第\(k\)个物品。如果\(k\)不可以选,有 f[i][j]+=f[i+1][j]
;否则,即\(k\)可以选,有f[i][j]+=max(f[i+1][j],f[i+1][j|(1<<k)]+val[k])
。
这时期望就可以正常除以\(n\)了,因为刷到所有物品的概率是均等的。
复杂度\(O(k^22^n)\)。
代码:
#include<bits/stdc++.h>
using namespace std;
int n,m,lim,val[16],sta[16];
double f[110][1<<16];
int main(){
scanf("%d%d",&m,&n),lim=(1<<n);
for(int i=0,x;i<n;i++){
scanf("%d",&val[i]);
scanf("%d",&x);
while(x)sta[i]|=(1<<(x-1)),scanf("%d",&x);
}
for(int i=m;i;i--)for(int j=0;j<lim;j++){
for(int k=0;k<n;k++)if((j&sta[k])==sta[k])f[i][j]+=max(f[i+1][j],f[i+1][j|(1<<k)]+val[k]);else f[i][j]+=f[i+1][j];
f[i][j]/=n;
}
printf("%lf\n",f[1][0]);
return 0;
}