题意:Tomato要在服务器上激活一个游戏,一开始服务器序列中有N个人,他排在第M位,每次服务器会对序列中第一位的玩家进行激活,有四种结果:
1.有p1的概率会激活失败,这时候序列的状态是不变的。
2.有p2的概率第一位的玩家会连接错误,这时候序列中第一位的玩家会成为最后一位,其他玩家相对位置不变。
3.有p3的概率第一位的玩家激活成功,这时候第一位的玩家会离开序列。
4.有p4的概率服务器崩溃,发生这件事之后所有玩家都不能激活了。
求Tomato遇到服务器崩溃并且在服务器崩溃时处于前K位的概率。
解法:这道题是一道非常好的概率DP题。必须一步一步来全部理解之后自己写一遍。
首先是设计状态dp[i]j[]代表队伍有i个人主角现在拍第j的概率。
写出状态转移方程(搭配题意写):
j==1 ,dp[i][j]=p1dp[i][j]+p2dp[i][i]
1<j<=k ,dp[i][j]=p1dp[i][j]+p2dp[i][j-1]+p3dp[i-1][j-1]+p4
j>k ,dp[i][j]=p1dp[i][j]+p2dp[i][j-1]+p3dp[i-1][j-1]
初步化简上诉式子:
其中 pp=1/(1-p1)
j==1 ,dp[i][j]=pp*p2dp[i][i]
1<j<=k ,dp[i][j]=pp*p2dp[i][j-1]+pp*p3dp[i-1][j-1]+pp*p4
j>k ,dp[i][j]=pp*p2dp[i][j-1]+pp*p3dp[i-1][j-1]
深度抽象上诉式子:
j==1 ,dp[i][j]=k[j] * dp[i][i] + b[j]
1<j<=k ,dp[i][j]=k[j] * dp[i][j-1] + b[j]
j>k ,dp[i][j]=k[j] * dp[i][j-1] + b[j]
(为什么是这样:我们仔细观察初步化简的式子:每一个方程右边除了dp[i][i]/dp[i][j-1]是与i同阶的,后面的都是i-1阶的,因为我们是一层一层做dp的,也就是说后面的可以看成是常数,所以这里的kj和bj具体是什么我们先根据初步化简的式子求出来放到数组里当成常数然后就不用管了)
我们仔细观察上诉深度化简的式子,发现其实第i阶就是有n个变量dp[i][j],然后有n个不同的方程,那么就一定可以用高斯消元解出方程!!!
但是再次仔细观察发现:欸!这n个方程结构很简单恰好形成了个环形!!那么我们就可以利用这点直接先解出一个未知数,然后递推出全部的未知数!
怎么解?
我们用笨方法模拟一下:假设只有3个未知数
dp1=k1dp3+c1
dp2=k2( k1dp3 + c1 ) +c2
dp3=k3( k2( k1dp3 + c1 ) +c2 ) + c3
我们尝试把dp3化简: dp3=k3k2k1dp3 + k3k2c1 + k3c2 + c3
那么规律不就出来了吗!
于是我们解出dp[i][i]再解出dp[i][1],然后就可以愉快地递推后面的值了。
#include<bits/stdc++.h> using namespace std; const int N=2e3+10; const double eps=1e-8; int n,m,K; double p1,p2,p3,p4,pp,p14; double k[N],b[N],dp[N][N]; void init() { for (int i=1;i<=n;i++) k[i]=b[i]=0; for (int i=1;i<=n;i++) for (int j=1;j<=n;j++) dp[i][j]=0; } int main() { while (cin>>n && n) { cin>>m>>K; cin>>p1>>p2>>p3>>p4; init(); if (fabs(p4)<eps) { printf("%.5lf\n",0); continue; } pp=1.0/(1.0-p1); p14=1.0/(1.0-p1-p4); for (int i=1;i<=n;i++) k[i]=pp*p2; dp[1][1]=p4/(1-p1-p2); for (int i=2;i<=n;i++) { b[1]=pp*p4; for (int j=2;j<=K;j++) b[j]=pp*p3*dp[i-1][j-1]+pp*p4; for (int j=K+1;j<=i;j++) b[j]=pp*p3*dp[i-1][j-1]; double kk=1.0,bb=0; for (int j=i;j;j--) { bb+=kk*b[j]; kk=k[j]*kk; } dp[i][i]=bb/(1.0-kk); dp[i][1]=k[1]*dp[i][i]+b[1]; for (int j=2;j<i;j++) dp[i][j]=k[j]*dp[i][j-1]+b[j]; } printf("%.5lf\n",dp[n][m]); } return 0; }