比较恶心的几率(指望)+状压DP,想正推2H的我瑟瑟发抖git
因为数据范围不大,所以咱们能够直接状压每一个宝物取或不取的状况,设\(f_{i,j}\)表示前\(i\)轮且宝物是否取过的状态为\(j\)时的方案总数,可是咱们发现这样可能会致使一些不合法的状态也获得转移,所以咱们考虑倒推spa
用\(f_{i,j}\)表示表示在第\(1\)轮到第\(i-1\)轮内宝物是否取过的状态为\(j\),第\(i\)轮到第\(k\)轮的最大指望得分,那么这样就能够经过倒推动行转移了。code
具体转移的时候咱们枚举全部的宝物限制,那么转移就很明显了it
不过因为这里要求的是指望值,而每一次须要除以\(n\),最后的\(f_{1,0}\)即为答案io
CODEclass
#include<cstdio> #include<cctype> using namespace std; typedef double DB; const int N=16,INF=-1e9; int n,p[N],m,s[N],x,tot; DB f[105][(1<<N)+5]; inline char tc(void) { static char fl[100000],*A=fl,*B=fl; return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++; } inline void read(int &x) { x=0; char ch; int flag=1; while (!isdigit(ch=tc())) flag=ch^'-'?1:-1; while (x=(x<<3)+(x<<1)+ch-'0',isdigit(ch=tc())); x*=flag; } inline int calc(int x) { int res=0; while (x) res+=x&1,x>>=1; return res; } inline DB max(DB a,DB b) { return a>b?a:b; } int main() { //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout); register int i,j,k; read(m); read(n); tot=(1<<n)-1; for (i=0;i<n;++i) { read(p[i]); read(x); while (x) s[i]|=(1<<x-1),read(x); } for (i=m;i>=1;--i) for (j=0;j<=tot;++j) { for (k=0;k<n;++k) if ((s[k]&j)==s[k]) f[i][j]+=max(f[i+1][j],f[i+1][j|(1<<k)]+p[k]); else f[i][j]+=f[i+1][j]; f[i][j]=(DB)f[i][j]/n; } return printf("%.6lf",f[1][0]),0; }