结束状态很多 , 而初始状态是确定的 , 所以考虑从后往前\(DP\)或者从前往后记忆化搜索
转移可以参考[CH3803]扑克牌的记忆化搜索做法 , 而本题可以用\(DP\)
设\(f[i][S]\)表示在第\(1\)轮到第\(i-1\)轮内宝物是否取过的状态为\(S\),第\(i\)轮到第\(K\)轮的最大期望得分
答案就是\(f[1][0]\)
状态的转移无非就是(轮数,已选状态)之间的递推 ; 还有就是一定要把题看清 , 比如这里的每个物品只能得一次分
如果可以取 , \(f[i][S]+=max(f[i+1][S],f[i+1][S|(1<<k-1)]+P_k)\)
否则不能取 , 即\(f[i][S]+=f[i+1][S]\)
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define debug(...) fprintf(stderr,__VA_ARGS__)
#define Debug(x) cout<<#x<<"="<<x<<endl
using namespace std;
typedef long long LL;
const int INF=1e9+7;
inline LL read(){
register LL x=0,f=1;register char c=getchar();
while(c<48||c>57){if(c=='-')f=-1;c=getchar();}
while(c>=48&&c<=57)x=(x<<3)+(x<<1)+(c&15),c=getchar();
return f*x;
}
const int MAXN=17;
const int MAXM=105;
double f[MAXM][1<<15],ans;
int p[MAXN],sta[MAXN],n,m,x;
int main(){
m=read(),n=read();
for(int i=1;i<=n;i++){
p[i]=read();
while(x=read()) sta[i]|=1<<(x-1);
}
for(int i=m;i>=1;i--)
for(int j=0;j<(1<<n);j++){
for(int k=1;k<=n;k++){
if((j&sta[k])==sta[k]) f[i][j]+=max(f[i+1][j],f[i+1][j|(1<<(k-1))]+p[k]);
else f[i][j]+=f[i+1][j];
}
f[i][j]/=n;//求的是期望,所以还要/n
}
printf("%.6lf\n",f[1][0]);
}