有生以来见过的最恶心的 DP 题目。


题意:n 个人排队激活仙剑奇侠 5. 服务器会按队列顺序进行激活操作。但是在激活队首顾客时,会有以下 4 中情况:

1. 激活失败,重试:概率 p1
2. 链接丢失,队首顾客被扔到队尾:概率 p2
3. 激活成功:队首顾客消失
4. 服务器错误:服务器被关闭,队列里的所有顾客的请求失效。

现在排在第 m 个的人想知道,有多大的可能性他前面有少于 k 个人时服务器关闭,因为这样是很操蛋的 (原文是 suck)


思路:很直接想到用 f[i][j] 表示有 i 个人排队站在第 j 个时出现 suck 情况的概率。那么… 然后一阵沉默.

\begin{equation}
f[i][1]=f[i][1]P_1+f[i][i]P_2+P_4
\end{equation}
\begin{equation}
f[i][j]=f[i][j]P_1+f[i][j-1]P_2+f[i-1][j-1]P_3 (j>k)
\end{equation}
\begin{equation}
f[i][j]=f[i][j]P_1+f[i][j-1]P_2+f[i-1][j-1]P_3+P_4 (j<=k)
\end{equation}

移项。

令 $P_{21}=\frac{P_2}{1-P_1}$,$P_{31}=\frac{P_3}{1-P_1}$,$P_{41}=\frac{P_4}{1-P_1}$

设一个数组 C[]

$$
\begin{cases}
C[j]&f[i-1][j-1]P_{31}(j>k) \\
C[j]&f[i-1][j-1]P_{31}+P_{41}(j<=k)
\end{cases}
$$

那么

$$
f[i][1]=f[i][i] P _ {21}+ P _ {41}\\
f[i][j]=f[i][j-1] P _ {21}+C[j]
$$

这样我们就可以递推了

吗?

由于 f[i][1] 和 f[i][i] 互相依赖,所以呵呵。

于是我们得手动列出 f[i][j] 的表达式,手动代入消元

然后可以得到:

$$
f[i][i]=\frac{\sum{P _ {21}^{i-x}C[x]} _ {x=1}^{i}}{1-P _ {21}^i}
$$

然后就可以用 f[i][i] 开始递推了

#include <iostream>
#include <cstdio>
#include <cstring>

#define MX 2009

using namespace std;

double f[MX][MX];
double c[MX];
int n,m,tar;
double p1,p2,p3,p4,p21,p31,p41;
double sum,p;

int main()
{
    while(scanf("%d%d%d%lf%lf%lf%lf",&n,&m,&tar,&p1,&p2,&p3,&p4)!=EOF)
    {
        if(p4<=0.000001){printf("0.00000\n");continue;}
        p21=p2/(1-p1),p31=p3/(1-p1),p41=p4/(1-p1);
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=i;j++)c[j]=(j>tar?f[i-1][j-1]*p31:f[i-1][j-1]*p31+p41);
            sum=0,p=1;
            for(int j=0;j<=i-1;j++)sum+=c[i-j]*p,p*=p21;
            f[i][i]=sum/(1-p);
            f[i][1]=f[i][i]*p21+p41;
            for(int j=2;j<=i;j++)f[i][j]=f[i][j-1]*p21+c[j];
        }
        printf("%.5f\n",f[n][m]);
    }
    return 0;
}

分类: 文章

0 条评论

发表回复

Avatar placeholder

您的电子邮箱地址不会被公开。 必填项已用 * 标注