前言:期望dp状态的定义是较为显然的,但对于状态的转移往往需要一些公式的推导。关键的几点是状态之间的互通性,和状态转移的花费,以及转移的概率
解决期望dp的几个技巧如下:
E[X+Y]=E[X]+E[Y]
我们所求的期望可以化为多个步骤的期望累和
相关题目:J,L
在目标确定的情况下,可以得知在目标到达目标的期望值为0,然后根据期望的线性性质怎么又是线性性质 得到状态转移方程:
dp[i]+=(dp[j]+cost)*p cost是转移的花费 p是概率 有时要除以(1-转移到自己的概率,但这是又不能单纯地+cost)所以要结合具体情况考虑
相关题目:A,C,D,E
E[A]=a1*p1+a2*p2+…+an*pn;
a1,a2…an为各种方案(情况)的花费(权值),p1,p2…pn为对应方案(情况)的发生概率 所以在最终方案确定且较少时,只用计算出发生的概率,在最后再乘上权值求累和就好了
相关题目:G,J
实际上,更多的题目需要自己推出公式,并按照推出的规律进行一些状态转移
像这种题,难度就会略高一些:
相关题目:B,H,I
题目选讲:
B:参见Komachi dalao博客
G:
看到 220 就应该知道可以用状压,
但如果单纯地算每个数被计算出的概率,那么复杂度为 n∗2n∗T ,稳定吃Tle
题目的另外一个特别之处在于:只有|,&,^三个运算符,他们在运算时是不会发生进位的,所以可以直接记录每一位数出现0或1的概率,然后在最后直接概率乘上权值就好了
参见代码:
#include
double dp[205][25][2],P[205];
int A[205];
int B[205][25];
char op[205][3];
int main(){
int n,cnt=0;
while(~scanf("%d",&n)){
cnt++;
for(int i=1;i<=n+1;i++)
scanf("%d",&A[i]);
for(int i=1;i<=n;i++)scanf("%s",op[i]);
for(int i=1;i<=n;i++)scanf("%lf",&P[i]);
for(int i=0;i<=20;i++)for(int j=0;j<=n;j++)for(int k=0;k<2;k++)dp[j][i][k]=0;
for(int k=1;k<=n+1;k++)
for(int i=0;i<=20;i++)
if(A[k]&(1<1;
else B[k][i]=0;
for(int i=0;i<=20;i++)
if(B[1][i])dp[0][i][1]=1;
else dp[0][i][0]=1;
for(int i=1;i<=n;i++){
for(int j=0;j<=20;j++){
double p=1-P[i];
if(op[i][0]=='&'){
if(B[i+1][j]==0)dp[i][j][0]+=(dp[i-1][j][0]+dp[i-1][j][1])*p;
else {
dp[i][j][1]+=dp[i-1][j][1]*p;
dp[i][j][0]+=dp[i-1][j][0]*p;
}
}
else if(op[i][0]=='|'){
if(B[i+1][j]==0){
dp[i][j][0]+=dp[i-1][j][0]*p;
dp[i][j][1]+=dp[i-1][j][1]*p;
}
else dp[i][j][1]+=(dp[i-1][j][1]+dp[i-1][j][0])*p;
}
else if(op[i][0]=='^'){
if(B[i+1][j]==0){
dp[i][j][0]+=dp[i-1][j][0]*p;
dp[i][j][1]+=dp[i-1][j][1]*p;
}
else {
dp[i][j][0]+=dp[i-1][j][1]*p;
dp[i][j][1]+=dp[i-1][j][0]*p;
}
}
dp[i][j][1]+=dp[i-1][j][1]*P[i];
dp[i][j][0]+=dp[i-1][j][0]*P[i];
}
}
double ans=0;
for(int j=0;j<=20;j++)
ans+=dp[n][j][1]*(1<printf("Case %d:\n%.6f\n",cnt,ans);
}
return 0;
}
J:
可以知道,对于m道题的每n道题,会对应不同的n个人
那么可以直接对这n个人对应n道题进行状压dp,复杂度 T∗m∗2n
根据期望的线性性质,我们可以把每个阶段的期望值都加起来,然后就可以得到答案
#include
double dp[15][1200];
double P[15][1005];
template <class _> _ max(_ x,_ y){return x>y?x:y;}
template <class _> _ min(_ x,_ y){return xint main(){
int T,t=0;
scanf("%d",&T);
while(T--){
int n,m;
scanf("%d %d",&n,&m);
for(int i=0;ifor(int j=1;j<=m;j++)
scanf("%lf",&P[i][j]);
double ans=0;
int cnt=(m-1)/n+1,tta=(1<1;
for(int k=0;kint len=min(m,k*n+n);
len-=k*n;
for(int i=0;i<=len;i++)for(int j=0;j<=tta;j++)dp[i][j]=-1;
dp[0][0]=0;
for(int i=1;i<=len;i++){//k*n+i
for(int j=0;j<=tta;j++){
if(dp[i-1][j]==-1)continue;
for(int h=0;hif((j&(1<continue;
dp[i][j|(1<1<1][j]+P[h][k*n+i]);
}
}
}
double sum=0;
for(int i=0;i<=tta;i++)
if(dp[len][i]>sum)sum=dp[len][i];
ans+=sum;
}
printf("Case #%d: %.5f\n",++t,ans);
}
return 0;
}
L:
仍是根据期望的线性性质,求被覆盖点的期望,可以化简为求所有点被覆盖的期望的累和。然后利用容斥,算出点不被覆盖的概率p,然后 1−pk 即为该点被覆盖的概率,也是该点的期望值(由于贡献为1)
#include
int main(){
int T,t=0;
scanf("%d",&T);
while(T--){
int n,m,k;
scanf("%d%d%d",&n,&m,&k);
long long tmp=1ll*n*n*m*m;
double ans=0;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
long long s=0;
s+=1ll*n*n*(j-1)*(j-1);
s+=1ll*m*m*(i-1)*(i-1);
s+=1ll*n*n*(m-j)*(m-j);
s+=1ll*m*m*(n-i)*(n-i);
s-=1ll*(i-1)*(i-1)*(j-1)*(j-1);
s-=1ll*(i-1)*(i-1)*(m-j)*(m-j);
s-=1ll*(n-i)*(n-i)*(m-j)*(m-j);
s-=1ll*(n-i)*(n-i)*(j-1)*(j-1);
double p=1.0*s/tmp;
double d=1;
for(int h=1;h<=k;h++)d=d*p;
ans+=1-d;
}
}
printf("Case #%d: %.f\n",++t,ans);
}
return 0;
}