Codeforces 518D Ilya and Escalator 题解

题意简述

$n(<=2000)$个人上电梯。每一秒,队列的第一个人都有$p$(每个时刻都是$p$,不会变,$0<=p<=1$)的概率上电梯。请求出$t(<=2000)$秒时电梯上人数的期望值。

思路框架

dp[i][j]表示第$i$时刻有$j$个人在电梯上的概率。最后用概率乘以数值求出期望(根据定义)。

具体思路

老样子,分两块

Part1. 设状态

首先$dp$里面肯定有一维时间,这毫无疑问。然后我们发现,

  1. n,t只有2000,肯定不是让你线性过的,又不是A题
  2. 只有一维时间好像也无法转移
    所以考虑加上一维人数。粗略估计珂以转移。

Part2. 转移

$dp[i][j]$:时间为$i$,电梯上有$j$个人的概率。

$dp[i][j]=dp[i-1][j-1]\times p+dp[i-1][j]\times (1-p)$。其中$p$是上来一个人的概率,$1-p$是没上来人的概率。这个式子看起来很正确。但是我们考虑几个特殊情况

  1. j=0。这个的确需要考虑。而且这个时候j-1=-1,直接访问会RE。但是也不是不能转移,方程应该是dp[i][0]=dp[i-1][0]×(1-p)。也就是,我们要电梯上有0个人,那就要一直不上来,也就是一直乘以1-p。当然你也珂以认为它是(1-p)^i。
  2. j=n。如果上一秒已经到$n$个人了,这个时候队列已经空了。无论发生什么都还是$n$个人。所以$dp[i][n]=dp[i-1][n-1]\times p+dp[i-1][n]$。

特判一下转移即珂。边界很显然,$dp[0][0]=1.00$

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#include <bits/stdc++.h>
using namespace std;
namespace Flandre_Scarlet
{
#define N 2333
#define real double
#define F(i,l,r) for(int i=l;i<=r;++i)
#define D(i,r,l) for(int i=r;i>=l;--i)
#define Fs(i,l,r,c) for(int i=l;i<=r;c)
#define Ds(i,r,l,c) for(int i=r;i>=l;c)
#define Tra(i,u) for(int i=G.Start(u),__v=G.To(i);~i;i=G.Next(i),__v=G.To(i))
#define MEM(x,a) memset(x,a,sizeof(x))
#define FK(x) MEM(x,0)

int n,t;real p;
void Input()
{
scanf("%d%lf%d",&n,&p,&t);
}

real dp[N][N];
void Soviet()
{
FK(dp);
dp[0][0]=1.00;
F(i,1,t)
{
F(j,0,n)
{
if (j==0) dp[i][j]=dp[i-1][j]*(1-p);
else if (j==n) dp[i][j]=dp[i-1][j-1]*p+dp[i-1][j];
else dp[i][j]=dp[i-1][j-1]*p+dp[i-1][j]*(1-p);

// printf("dp[%d][%d]=%.3f\n",i,j,dp[i][j]);
}
}
real ans=0.00;
F(i,1,n) ans+=dp[t][i]*i;
printf("%.6f\n",ans);
}

#define Flan void
Flan IsMyWife()
{
Input();
Soviet();
}
}
int main()
{
Flandre_Scarlet::IsMyWife();
getchar();getchar();
return 0;
}
w