1489 玩纸牌
★ 输入文件:expected.in
输出文件:
expected.out
评测插件
时间限制:1 s 内存限制:256 MB
【题目描述】
本题目有一定的数学背景。
题中要求计算一个随机变量的期望值。如果你之前没有听说过这些数学名词,下面给出了一些简单的定义。一个随机变量是一个可以取若干个值的变量,对于每个可能值,它都有一定概率取这个值。取到每个可能值的概率都是正的,并且它们的和是1.随机变量的数学期望是它所有可能值与其对应概率之积的乘积总和(对它有一些更为复杂,形式化的定义,但你现在不需要用到这些)。例如,一个标准的6面骰子投出后上面的值是一个随机变量,有6个可能值(1到6),取到每个可能值的概率都是1/6.那么它的数学期望就是1/6+2/6+...+6/6=3.5.
下面是题目内容。
我喜欢玩纸牌接龙。每次我都有p的概率赢,1-p的概率输。游戏程序会统计我获胜盘数的百分比。如果我一直玩下去,这个百分比就会在p*100%左右浮动。但我仍不满足。
这是我的计划。每天,我都会玩纸牌接龙。如果我赢了,我就高高兴兴地去睡觉。如果我输了,我就一直玩下去直到我这天获胜盘数的百分比严格大于p。这时,我就会宣布胜利,然后高高兴兴地去睡觉。你可以看到,每天我都可以宣布自己保持了获胜比例大于p*100%。我打败了数学规律!
如果你感觉这里好像有什么奇怪的东西,那你就对了。我不可能永远这么做,因为我每天玩的游戏盘数有限。我每天至多玩n盘游戏。那么,这个机智的计划在因为这一限制失败前,执行天数的数学期望是多少?值得注意的是,答案至少为1,因为我至少要玩一天才能发现计划失败了。
【输入格式】
输入包含多组数据。
输入文件的第一行是数据组数N。
接下来是N组数据。每组数据有一行,包含p(写成分数)和n。
【输出格式】
对于每组数据,输出一行"Case #x: y",其中x是数据组数(从1开始),y是期望天数,向下取整。
【样例输入】
4
1/2 1
1/2 2
0/1 10
1/2 3
【样例输出】
Case #1: 2
Case #2: 2
Case #3: 1
Case #4: 2
【提示】
1<=N<=3000,0<=p<1
p的分母不超过1000。
1<=n<=100。
答案允许有±1的误差。
【来源】
刘汝佳,《算法竞赛入门经典训练指南》表2.8
题解:概率与期望。
我们可以发现每一天晚上的情况是相对独立互不影响的那么我就可以先考虑一天晚上的状况。
f[i][j]表示进行了i局一共赢了j局,且枚举结束后获胜比例都不超过p的概率。
那么就可以进行转移f[i][j]=f[i-1][j]*(1-p)+f[i-1][j-1]*p j/i<=p
f[i][j]=0 j/i>p
那么这天晚上结束的概率Q=f[n][0]+f[n][1]+...f[n][n]
然后我们考虑是第几天结束
x=1时结束的概率为Q
x=2时结束的概率为(1-Q)*Q
x=3时结束的概率为(1-Q)^2*Q.....
根据期望公式可得 Ex=权值*概率
Ex=Q+2*(1-Q)*Q+3*(1-Q)^2*Q+...
令s=Ex/Q=1+2*(1-Q)+3*(1-Q)^2+....
(1-Q)*s=(1-Q)+2*(1-Q)^2+3*(1-Q)^3+....
上面两式作差可得 s-(1-Q)s=1+(1-Q)+(1-Q)^2+....
Qs=Ex=1+(1-Q)+(1-Q)^2+(1-Q)^3+...
发现是个等比数列,公比为1-Q 当项数趋近于无穷的时候,等比数列的求和公式为a1/(1-q)
所以Ex=1/Q
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define N 303
using namespace std;
double f[N][N],p;
int n,t,a,b;
char s[N];
int main()
{
freopen("expected.in","r",stdin);
freopen("expected.out","w",stdout);
scanf("%d",&t);
for (int T=1;T<=t;T++){
scanf("%d/%d %d",&a,&b,&n);
p=(double)a/b;
memset(f,0,sizeof(f)); f[0][0]=1; f[0][1]=0;
for (int i=1;i<=n;i++)
for (int j=0;j*b<=i*a;++j){
f[i][j]=f[i-1][j]*(1-p);
if (j) f[i][j]+=f[i-1][j-1]*p;
}
double ans=0;
for (int j=0;j*b<=n*a;++j) ans+=f[n][j];
printf("Case #%d: %d\n",T,(int)(1/ans));
}
}