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]);
    }
}





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