题目:http://acm.hdu.edu.cn/showproblem.php?pid=4649
题目大意:给你n+1个数,有n个运算符,第i和i+1个数消失的概率为p[ i ],问你最后这个数的数学期望。
思路:因为只有20位,而且&,|,^都不会进位,那么一位一位地看,每一位不是0就是1,这样求出每一位是1的概率,再乘以该位的十进制数,累加,就得到了总体的期望。用d[ i ][ j ][ k ] 表示前i位前j个运算这个位为k的概率,k为0 or 1,状态转移方程:d[ i ][ j ][ k op t ] += SIMGA (d[ i ][ j ][ k ] * (1 - p[ j ])),这为没有消失,t为第j个数第i位的值,d[ i ][ j ][ k ] += SIMGA (d[ i ][ j ][ k ] * p[ j ]),这个是消失。数组可以用滚动数组。
上面是题解上的解法,比赛时我自己做的是用直接用 d[ i ][ s ] 表示前i个运算,值为s的概率,这样时间复杂度是O(n*(1<<20)),而上面的解法时间复杂度是O(20*n),快了好多啊,看来以后碰到位运算什么的题,先考虑一位一位按位来做。。 = =
代码如下:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int a[222];
int op[222];
double p[222];
double d[201][2];
char str[11] ;
int main()
{
int ca = 0 ;
int n;
while(~scanf("%d",&n))
{
for(int i = 0;i<=n;i++)
scanf("%d",&a[i]);
for(int i = 1;i<=n;i++)
{
scanf("%s",str);
if(str[0]=='&')
op[i] = 0;
else if(str[0]=='|')
op[i] = 1;
else op[i] = 2;
}
for(int i = 1;i<=n;i++)
scanf("%lf",&p[i]);
double ans = 0;
for(int i = 0;i<20;i++)
{
for(int j = 0;j<=n;j++)
d[j][0] = d[j][1] = 0;
d[0][(a[0]>>i)&1] = 1;
for(int j = 0;j<n;j++)
{
int t = ((a[j+1]>>i)&1);
if(op[j+1]==0)
{
for(int s = 0;s<=1;s++)
{
d[j+1][s&t] += d[j][s]*(1-p[j+1]);
d[j+1][s] += d[j][s]*p[j+1];
}
}
else if(op[j+1]==1)
{
for(int s = 0;s<=1;s++)
{
d[j+1][s|t] += d[j][s]*(1-p[j+1]);
d[j+1][s] += d[j][s]*p[j+1];
}
}
else
{
for(int s = 0;s<=1;s++)
{
d[j+1][s^t] += d[j][s]*(1-p[j+1]);
d[j+1][s] += d[j][s]*p[j+1];
}
}
}
//printf("i = %d,d = %f,%f\n",i,d[n][1],d[n][0]);
ans += d[n][1]*(1<<i);
}
printf("Case %d:\n%.6f\n",++ca,ans);
}
return 0;
}