题目: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; }