例题
A.Happy Running
题意: 操场总共下,跑圈需要先打卡A再打卡B,最终跑的距离超过k的概率。
![在这里插入图片描述](https://img-blog.csdnimg.cn/64acca246ddb46359ded44fe183e8f6e.jpeg
除此外,两种情况的
void solve()
{
double k,x;
scanf("%lf %lf",&k,&x);
double ans=0;
if(k<x)ans=(1.0-k*k/(x*x))*0.5+0.5;
else if(k==x)ans=0.5;
else if(k<2*x) ans=(2*x-k)*(2*x-k)/(2*x*x);
printf("%.2lf\n",ans);
return;
}
B.恶意竞争
题意: 两家公司竞争找bug,共有n分类和s个系统,一个bug属于一个分类和一个系统,问找到所欲分类和系统至少都有一个bug的期望天数。
设dp[i][i]是已经找到的bug又i种,属于j个系统的期望天数。
那么dp[n][s]=0,dp[0][0]就是答案
状态转移:
找到的bug有四种情况:
1.属于已有的i个分类和j个系统(i/n) * (j/s)*(dp[i][j]+1)
2.属于已有系统,不属于已有分类(i/n) * (1-j/s)*(dp[i][j+1]+1)
3.属于已有分类,不属于已有系统(1-i/n) * (j/s)*(dp[i+1][j]+1)
4.既不属于已有分类,也不属于已有系统(1-i/n) * (1-j/s)*(dp[i+1][j+1]+1)
再化简
void solve()
{
int n,s;
scanf("%d %d",&n,&s);
for(int i=n;i>=0;i--){
for(int j=s;j>=0;j--){
if(i==n&&j==s)continue;
dp[i][j]+=(double)n*s/(n*s-i*j);//已有的i个分类和j个系统
dp[i][j]+=(double)(n-i)*j*dp[i+1][j]/(n*s-i*j);//属于已有系统,不属于已有分类
dp[i][j]+=(double)i*(s-j)*dp[i][j+1]/(n*s-i*j);//属于已有分类,不属于已有系统
dp[i][j]+=(double)(n-i)*(s-j)*dp[i+1][j+1]/(n*s-i*j);//既不属于已有分类,也不属于已有系统
}
}
printf("%.5lf",dp[0][0]);
return;
}
C.带富翁
题意: 小明掷骰子,每走到一个点,就能够得到相应的点数,那么只能走到点数n,走到点数n的得分期望是多少?
设dp[i]为到i的得分期望
那么下一步能够得到i+1,i+2,i+3…i+6,只要是没有超过n就能够走到,并且每一种走法的概率都是1/6
那么当i+6>n时,筛子最多只能够掷到n-i,只有n-i种情况,每种情况
分类版:
void solve()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%lf",&a[i]);
dp[i]=a[i];
}
for(int i=n-1;i>=1;i--){
if(i+6<=n){//考虑当掷骰子为6都不会超过n,那么每个骰子都是等可能在下一次出现
for(int j=1;j<=6;j++)
dp[i]+=1.0/6*dp[i+j];
}
else{//否则下一个点数只可能有n-i种可能
for(int j=1;j<=n-i;j++)
dp[i]+=1.0/(n-i)*dp[i+j];
}
}
printf("%.8lf\n",dp[1]);
return;
}
简化版:
void solve()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%lf",&a[i]);
dp[i]=a[i];
}
for(int i=n-1;i>=1;i--){
double res=0;
for(int j=1;j<=6;j++){
if(i+j<=n)
res+=dp[i+j];
}
dp[i]+=res/min(6,n-i);
}
printf("%.8lf\n",dp[1]);
return;
}
D.筛子游戏
题意: 投三个骰子,点数分别能够为k1,k2,k3,每次能够得到点数和的得分。但是k1,k2,k3点数分别为a,b,c时就必须得分归零,问得分大于n的期望次数是多少。
设dp[i]是得分为i的次数期望
那么状态转移dp[i]=
(
∑
p
k
∗
d
p
[
i
+
k
]
)
+
d
p
[
0
]
∗
p
0
+
1
\displaystyle \left( \sum_{}p_k*dp[i+k] \right)+dp[0]*p_0+1
(∑pk∗dp[i+k])+dp[0]∗p0+1
p
k
表示加
k
的概率,
p
0
表示归
0
的概率
p_k表示加k的概率,p_0表示归0的概率
pk表示加k的概率,p0表示归0的概率
因为以n为分界点,所以从n反过来推,dp[0]就是答案
其实
(
∑
p
k
∗
d
p
[
i
+
k
]
)
\displaystyle \left( \sum_{}p_k*dp[i+k] \right)
(∑pk∗dp[i+k])里面也存在dp[0],这时候也能够将式子设成
dp[i]=A[i]dp[0]+B[i]
dp[i]=
(
∑
p
k
∗
A
[
i
+
k
]
∗
d
p
[
0
]
+
p
k
∗
B
[
i
+
k
]
)
+
d
p
[
0
]
∗
p
0
+
1
\displaystyle \left( \sum_{}p_k*A[i+k]*dp[0]+p_k*B[i+k] \right)+dp[0]*p_0+1
(∑pk∗A[i+k]∗dp[0]+pk∗B[i+k])+dp[0]∗p0+1=
(
∑
p
k
∗
A
[
i
+
k
]
+
p
0
)
∗
d
p
[
0
]
+
(
∑
p
k
∗
B
[
i
+
k
]
)
+
1
\displaystyle \left( \sum_{}p_k*A[i+k]+p_0 \right)*dp[0]+\left( \sum_{}p_k*B[i+k]\right)+1
(∑pk∗A[i+k]+p0)∗dp[0]+(∑pk∗B[i+k])+1
这样就方便求了
void solve()
{
int n,k1,k2,k3,a,b,c;
scanf("%d %d %d %d %d %d %d",&n,&k1,&k2,&k3,&a,&b,&c);
double p0=1.0/(k1*k2*k3);
for(int i=1;i<=k1;i++)
for(int j=1;j<=k2;j++)
for(int k=1;k<=k3;k++)
if(i==a&&j==b&&k==c)continue;
p[i+j+k]+=p0;
int k=k1+k2+k3;
for(int i=n;~i;i--){
for(int j=3;j<=k;j++){
A[i]+=p[j]*A[i+j];
B[i]+=p[j]*B[i+j];
}
B[i]++,A[i]+=p0;
}
printf("%.10lf\n",B[0]/(1-A[0]));
return;
}
E.食堂
题意:
吉吉国王偶尔会回想起自己的高中时代。在吉吉国王的高中时代,下课后冲向食堂是每个学生的基本操作,但是总得有人失败,为什么不能是我,实际上吉吉国王在打饭这件事上也是失败过很多次,比如没带饭卡,走错窗口,甚至食堂关门。
吉吉国王的高中食堂排队可以看成一个长度为nn的队列,一开始吉吉国王站在mm这个位置上,一般来说,窗口前的第一个人在打饭的时候会发生四种情况。
第一种情况是打饭的时候窗口没人,这个时候要等待一会儿,发生的概率是p1
第二种情况是发现自己没带饭卡,这个时候就要回去拿饭卡并且排到了队列的末尾,发生的概率是p2(这里认为每个人只有在即将打饭的时候才会去摸饭卡,只有这时才有发现自己没带饭卡的机会。)
第三种情况是打饭成功,这个时候队列的长度减一,发生的概率是p3
第四种情况是食堂关门,这个时候大家都不能打饭了,发生的概率是p4
吉吉国王老倒霉蛋了,经常在食堂关门的时候排在队伍的前面,因此他想知道这样的事件发生的概率。现在你需要告诉吉吉国王在食堂关门时他排在队伍的前k位的概率。
设dp[i][j]表示i个人排队,吉吉国王排在第j个位置,达到目标状态的概率(j<=i)
dp[n][m]就是所求的答案
j==1时,dp[i][1]=p1×dp[i][j]+p2×dp[i][i]+p4
2<=j<=k,dp[i][j]=p1×dp[i][j]+p2×dp[i][j-1]+p3×dp[i-1][j-1]+p4
k<j<=i,dp[i][j]=p1×dp[i][j]+p2×dp[i][j-1]+p3×dp[i-1][j-1]
式子两边都有左边的值,则要将式子化简
就需要提前求出p21=p2/(1-p2),p31=p3/(1-p3),p41=p4/(1-p4)…
void solve()
{
cin>>n>>m>>k>>p1>>p2>>p3>>p4;
double p21=p2/(1.0-p1),p31=p3/(1.0-p1),p41=p4/(1.0-p1);
for(int i=1;i<=n;i++){
vector<double>A(n+1),B(n+1);
A[1]=p21,B[1]=p41;
for(int j=2;j<=i;j++){
if(j<=k){
A[j]=A[j-1]*p21;
B[j]=B[j-1]*p21+dp[i-1][j-1]*p31+p41;
}
else{
A[j]=A[j-1]*p21;
B[j]=B[j-1]*p21+dp[i-1][j-1]*p31;
}
}
dp[i][i]=B[i]/(1-A[i]);//提前求出dp[i][i]
for(int j=1;j<i;j++){
if(j==1)dp[i][j]=dp[i][i]*p21+p41;
else if(j<=k)dp[i][j]=dp[i][j-1]*p21+dp[i-1][j-1]*p31+p41;
else dp[i][j]=dp[i][j-1]*p21+dp[i-1][j-1]*p31;
}
}
printf("%.5lf",dp[n][m]);
return;
}