欢迎访问~原文出处——博客园-zhouzhendong
去博客园看该题解
题目传送门 - BZOJ1076
题意概括
有n个东西,k次扔出来。每次等概率扔出其中一个。
你可以拿这个东西,但是有条件,得在拿到指定东西之后再拿,否则白拿。
拿到一个东西,会获得其权值。可以是负数。
题解
状压dp跑一发。
一开始想写正着dp的,因为我觉得这样听挺容易想的。
然而网上的大牛都说是倒着的。于是我倒着了。
方程是这样的:
dp[i][j]表示扔出来i次之后,状态为j,在这之后可以得到的最大收益。
dp[i][j]=(∑F[k])/n
F[k]分两种情况。如果在状态j的情况下可以取k,那么F[k] = max(dp[i+1][j],dp[i+1][j | (1<<k)] + v[k])
否则F[k] = dp[i+1][j]
根据意义,最后输出dp[0][0]即可。
代码
#include <cstring> #include <algorithm> #include <cstdio> #include <cstdlib> #include <cmath> using namespace std; const int N=15+5,M=100+5,S=(1<<15)+5; int n,m,v[N],pre[N]; double dp[M][S]; int main(){ scanf("%d%d",&m,&n); memset(pre,0,sizeof pre); for (int i=0,p;i<n;i++){ scanf("%d",&v[i]); while (scanf("%d",&p)&&p!=0) pre[i]|=1<<(p-1); } for (int i=m-1;i>=0;i--) for (int j=0;j<(1<<n);j++){ dp[i][j]=0; for (int k=0;k<n;k++) if ((pre[k]&j)==pre[k]) dp[i][j]+=max(dp[i+1][j],dp[i+1][j|(1<<k)]+v[k]); else dp[i][j]+=dp[i+1][j]; dp[i][j]/=n; } printf("%.6lf",dp[0][0]); return 0; }