链接:https://ac.nowcoder.com/acm/problem/210487
来源:牛客网
题目描述
吉吉国王偶尔会回想起自己的高中时代。在吉吉国王的高中时代,下课后冲向食堂是每个学生的基本操作,但是总得有人失败,为什么不能是我,实际上吉吉国王在打饭这件事上也是失败过很多次,比如没带饭卡,走错窗口,甚至食堂关门。
吉吉国王的高中食堂排队可以看成一个长度为nn的队列,一开始吉吉国王站在mm这个位置上,一般来说,窗口前的第一个人在打饭的时候会发生四种情况。
第一种情况是打饭的时候窗口没人,这个时候要等待一会儿,发生的概率是p1 。
第二种情况是发现自己没带饭卡,这个时候就要回去拿饭卡并且排到了队列的末尾,发生的概率是p2 。(这里认为每个人只有在即将打饭的时候才会去摸饭卡,只有这时才有发现自己没带饭卡的机会。)
第三种情况是打饭成功,这个时候队列的长度减一,发生的概率是p3
第四种情况是食堂关门,这个时候大家都不能打饭了,发生的概率是p4
吉吉国王老倒霉蛋了,经常在食堂关门的时候排在队伍的前面,因此他想知道这样的事件发生的概率。现在你需要告诉吉吉国王在食堂关门时他排在队伍的前kk位的概率。
输入描述:
一行七个数表示n, m, k, p_1, p_2, p_3, p_4n,m,k,p_1 ,p_2 ,p_3 ,p_4 。
输出描述:
输出一个小数表示答案,小数点后保留五位。
示例1
输入
4 4 1 0.372818 0.318286 0.220035 0.0888615
输出
0.15428:
这道题的代码不长,但是在推公式的时候用了很多常见的套路:
定义状态f[i][j]表示一共i个人的队伍里牛牛处在位置j处的结果
首先,所有概率都是对队首的第一个人说的,因此我们可以写出状态转移方程:
if j==1
f[i][j] = p1*f[i][j] + p2*f[i][i] + p4
此时在满足要求的位置,因此可以等待p1,第一个人(此时为牛牛)忘带饭卡p2,食堂关门p4
if j>=2&&j<=k
f[i][j]=p1*f[i][j]+p2*f[i][j-1]+p3*f[i-1][j-1]+p4
此时在满足要求的位置,因此可以等待p1,第一个人忘带饭卡p2,第一个人打饭成功,食堂关门p4
if j>k
f[i][j]=p1*f[i][j]+p2*f[i][j-1]+p3*f[i-1][j-1]
此时在满足要求的位置,因此可以等待p1,第一个人忘带饭卡p2,第一个人打饭成功
由于两边都有f[i][j],那么经过移项合并同类项可以得到:
j==1
f[i][1]=p2/(1-p1)*f[i][i]+p4/(1-p1)
j<=k
f[i][j]=p2/(1-p1)*f[i][j-1]+p3/(1-p1)*f[i-1][j-1]+p4/(1-p1)
j<=n
f[i][j]=p2/(1-p1)*f[i][j-1]+p3/(1-p1)*f[i-1][j-1]
因此我们令:
a=p2/(1-p1)
b=p3/(1-p1)
c=p4/(1-p1)
观察式子发现当计算到f[i][j]的时候,f[i-1][j-1]已经被算出来了,因此我们可以将其看作常数项,继续定义化简:
j==1
d[j]=c
j<=k
d[j]=b*f[i-1][j-1]+c
j<=n
d[j]=b*f[i-1][j-1]
最后式子就被化简为了:
j==1
f[i][1] = a*f[i][i]+d[1]
j>=2
f[i][j]=a*f[i][i-1]+d[j]
多列几项f[i][j]可以发现从第一个式子开始全部带进最后一个式子可以算出f[i][i],因此先算出f[i][i]:
f[i][i]=(∑p[i-k]d[k])/(1-p[i])
然后再递推求出其他f[i][j]即可~
#include<bits/stdc++.h>
using namespace std;
const int N = 2010;
const double eps = 1e-9;
int n,m,k;
double p1,p2,p3,p4,f[N][N];
double p[N],d[N],a,b,c;
int main(){
cin >> n >> m >> k >> p1 >> p2 >> p3 >> p4;
if(fabs(p1-1)<eps){cout << "1.00000\n" << endl ; return 0;}
if(p4<eps) {cout << "0.00000\n" << endl; return 0;}
a = p2/(1.0-p1) , b = p3/(1.0-p1) , c = p4/(1.0-p1);
p[0]=1;
for(int i=1;i<=n;i++)
p[i]=p[i-1]*a;
f[1][1] = p4 / (1.0-p1-p2);
for(int i=2;i<=n;i++){
for(int j=1;j<=i;j++){
//先求d[j]
if(j==1) d[j]=c;
else if(j>=2&&j<=k) d[j]=b*f[i-1][j-1]+c;
else d[j]=b*f[i-1][j-1];
}
//再求f[i][i]
for(int j=1;j<=i;j++)
f[i][i]+=d[j]*p[i-j];
f[i][i]=(double)f[i][i]/(1.0-p[i]);
//递推
for(int j=1;j<=i;j++)
{
if(j==1) f[i][j]=a*f[i][i]+d[1];
else f[i][j]=a*f[i][j-1]+d[j];
}
}
printf("%.5lf",f[n][m]);
return 0;
}