HDU 4089 Activation [有环的概率DP]

Description

After 4 years' waiting, the game "Chinese Paladin 5" finally comes out. Tomato is a crazy fan, and luckily he got the first release. Now he is at home, ready to begin his journey. 
But before starting the game, he must first activate the product on the official site. There are too many passionate fans that the activation server cannot deal with all the requests at the same time, so all the players must wait in queue. Each time, the server deals with the request of the first player in the queue, and the result may be one of the following, each has a probability: 
1. Activation failed: This happens with the probability of p1. The queue remains unchanged and the server will try to deal with the same request the next time. 
2. Connection failed: This happens with the probability of p2. Something just happened and the first player in queue lost his connection with the server. The server will then remove his request from the queue. After that, the player will immediately connect to the server again and starts queuing at the tail of the queue. 
3. Activation succeeded: This happens with the probability of p3. Congratulations, the player will leave the queue and enjoy the game himself. 
4. Service unavailable: This happens with the probability of p4. Something just happened and the server is down. The website must shutdown the server at once. All the requests that are still in the queue will never be dealt. 
Tomato thinks it sucks if the server is down while he is still waiting in the queue and there are no more than K-1 guys before him. And he wants to know the probability that this ugly thing happens. 
To make it clear, we say three things may happen to Tomato: he succeeded activating the game; the server is down while he is in the queue and there are no more than K-1 guys before him; the server is down while he is in the queue and there are at least K guys before him. 
Now you are to calculate the probability of the second thing. 


题意:

现在一队人在排队等待进入游戏,但是服务器每一个时刻有概率发生下面4种事件:

P1的概率发生 队首的人进入游戏失败,下一时刻继续进入(相等于什么都不发生)

P2的概率发生 队首的人掉线,但是他会马上重连,并进入队尾

P3的概率发生 队首的人成功连入游戏,这个人会从队伍中消失

P4的概率发生 服务器挂掉.....

现在Tomato正在排的队伍有N个人,他现在在第M个,如果服务器挂掉的时候,他没有进入游戏,并且他正好在队伍的前K个,他会感到很遗憾,问他最终的遗憾的概率。


范围:

n,m,k<=2000


解法:

首先根据范围比较容易想到去概率DP,应该是N^2的复杂度,然后去设想状态,这个问题应该从边界条件入手

可以发现最终如果队伍中只剩1个人,且是Tomato,那么他遗憾的概率为 P4 /(1-P1-P2) (移项得到)

那么可以设DP[I][J]表示当前队伍还剩I个人,Tomato正好站在第J个,最终遗憾的概率为DP。


观察第一组样例,可以画出DP转移图:(手工画的,比较丑)


可以发现这个dp过程是有环的,对于结构较简单的有环DP,我们一般的处理方式是设未知数。

首先列出DP方程

DP[I][J] = P1*DP[I][J] + P2*DP[I][J-1] + P3*DP[I-1][J-1] + (J<=K ? P4 : 0) ; (其中 DP[I][J-1] 这部分,当J=1时,J-1应该换成I )

移项得 DP[I][J] =( P2*DP[I][J-1]  + P3*DP[I-1][J-1] + (J<=K ? P4 : 0)   ) /(1-P1)

发现DP[I-1][J-1]与自己无关,但是DP[I][J-1]会包含一部分自己(如图中的DP[2][2],P2概率走到DP[2][1],但是DP[2][1]会重新走回DP[2][2])

故设 DP[I][I] 为x(可以设环中的任意一个)

且接下来的讨论中,I是固定的。

那么  DP[I][J]  可以表示为 a[I][J]*x + b[I][J] (因为DP[I-1][J-1] 和 P4 这部分在DP的时候是已知的,所以可以看做是常数 b)

根据上面的方程

a[I][J] = a[I][J-1]*P2  /(1-P1)

b[I][J] = ( b[I][J-1]*P2 + DP[I-1][J-1]*P3 + (J<=K ? P4 : 0) ) / (1-P1)

最后根据DP[I][I] = a[i][i]*dp[i][i] + b[i][i] ,解出DP[I][I],并代入到其他方程,解出所有的DP[I][J] 

然后枚举下一个I,直到解出所有的概率。

最后的DP[N][M],便是答案,有一个坑点是P4等于0的时候,直接输出0.00000,否则DP过程有可能会出错(例如P1=1,那么被除数就是0)。


代码:

因为卡内存,所以使用了滚动数组

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<math.h>
#include<iostream>
#include<stdlib.h>
#include<set>
#include<map>
#include<queue>
#include<vector>
#include<bitset>
#pragma comment(linker, "/STACK:1024000000,1024000000")
template <class T>
bool scanff(T &ret){ //Faster Input
    char c; int sgn; T bit=0.1;
    if(c=getchar(),c==EOF) return 0;
    while(c!='-'&&c!='.'&&(c<'0'||c>'9')) c=getchar();
    sgn=(c=='-')?-1:1;
    ret=(c=='-')?0:(c-'0');
    while(c=getchar(),c>='0'&&c<='9') ret=ret*10+(c-'0');
    if(c==' '||c=='\n'){ ret*=sgn; return 1; }
    while(c=getchar(),c>='0'&&c<='9') ret+=(c-'0')*bit,bit/=10;
    ret*=sgn;
    return 1;
}
#define inf 1073741823
#define llinf 4611686018427387903LL
#define PI acos(-1.0)
#define lth (th<<1)
#define rth (th<<1|1)
#define rep(i,a,b) for(int i=int(a);i<=int(b);i++)
#define drep(i,a,b) for(int i=int(a);i>=int(b);i--)
#define gson(i,root) for(int i=ptx[root];~i;i=ed[i].next)
#define tdata int testnum;scanff(testnum);for(int cas=1;cas<=testnum;cas++)
#define mem(x,val) memset(x,val,sizeof(x))
#define mkp(a,b) make_pair(a,b)
#define findx(x) lower_bound(b+1,b+1+bn,x)-b
#define pb(x) push_back(x)
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;



double a[2][2020];
double b[2][2020];
double dp[2][2020];

double p1,p2,p3,p4;
int n,m,k;
int main(){
    while(scanf("%d",&n)!=EOF){
        scanff(m);scanff(k);
        scanff(p1);scanff(p2);
        scanff(p3);scanff(p4);
        if(p4==0.0){
            printf("%.5lf\n",0);
            continue;
        }
        a[0][0]=a[1][0]=1.0;
        dp[1][1]=p4/(1.0-p1-p2);
        rep(i,2,n){
            rep(j,1,i){
                a[i&1][j]=b[i&1][j]=dp[i&1][j]=0;
                b[i&1][j]=b[i&1][j-1]*p2;
                a[i&1][j]=a[i&1][j-1]*p2;
                b[i&1][j]+=p3*dp[(i-1)&1][j-1];
                if(j<=k)b[i&1][j]+=p4;
                a[i&1][j]/=1.0-p1;
                b[i&1][j]/=1.0-p1;
            }
            dp[i&1][i]=b[i&1][i]/(1.0-a[i&1][i]);
            rep(j,1,i-1){
                dp[i&1][j]=dp[i&1][i]*a[i&1][j]+b[ i&1][j];
            }
        }
        printf("%.5lf\n",dp[n&1][m]);
    }
}





發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章