HDU 4326Game(比較難理解的概率dp)

果7發表於2013-09-10

Game

Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 229    Accepted Submission(s): 85


Problem Description
There are N people playing a game. The rules of the game are described as follows:
Initially, there are N people numbered from 1-N. And they are arranged in a queue by the order from 1-N. Each round, only 4 people get into the game and each people has equally probability to win the game. The winner can continue to games, the loser will go to the end of the queue according to the order before this round (if someone was the winner before this round, we can consider he was the head of the queue). 
The first round of game, the first four people start to play the game. If someone continuously wins the game M times, he will become the final winner.
Now I want to know the probability for the K-th people to become the final winner.
 

Input
The first line of input contains T, the number of test cases.
Flowing T line, each line contains 3 integer N, M, K.(4<=N<=10, M<=10,K<=N)
 

Output
Each output should occupy one line. Each line should start with "Case #i : ", followed by the answer round to six decimal places.
 

Sample Input
3 4 1 1 5 1 5 5 2 1
 

Sample Output
Case #1: 0.250000 Case #2: 0.000000 Case #3: 0.217626
 

Author
BJTU
 

Source
 

Recommend
zhoujiaqi2010
 


題目大意:給出n個人每次4人進行比賽其他人等待,勝者繼續,負者排到最後,連續或得m次勝利的人成為最終的贏家,求第k個人最終獲得勝利的概率是多少?對於這題,我們首先確立一個這樣的模型: x1先贏了i局,正在於x2,x3,x4賭鬥,後面依次有x5,x6,……,xn等待。用P[i][j]表示x1先贏了i局的情況下,當前的xj獲勝的概率。

腦袋不靈光了,坑了一晚上才坑出來。。
下面的一段文字轉自茂茂:可以參見大牛的部落格

因為要考慮連續贏的情況並且每次動作只和第一個人有關,設第一維表示第一個人連續贏的次數。第二維表示第j個人此次贏的概率。

dp[i][j]表示第一個人已經贏了i次,當前第j個人能贏的概率。

最終也就是要求dp[0][k].表示第一個人一次都沒贏時第k個人贏的概率。

當j=1時,dp[i][j]=1/4*dp[i+1][j]+3/4*dp[1][n-2]    //該人要麼贏,要麼輸,輸的話,後面有兩個人排在他後面,所以他在n-2的位置。

當j=2時,dp[i][j]=1/4*dp[i+1][n-2]+1/4*dp[1][j-1]+2/4*dp[1][n-1]

當j=3時,dp[i][j]=1/4*dp[i+1][n-1]+1/4*dp[1][n-1]+1/4*dp[1][1]+1/4*dp[1][n]

當j=4時,dp[i][j]=1/4*dp[i+1][n]+2/4*dp[1][n]+1/4*dp[1][1];

當j>4時,dp[i][j]=1/4*dp[i+1][j-3]+3/4*dp[1][j-3]  

注意

1、i<m,

2、dp[m][1]=1,表示第一個人已經贏了m次,結束。

此轉移方程,前後都有,不能直接通過遞推或迭代求出,所以選用高斯消元求解。

一共有m*n個未知數,所以可以求一個n*m元的一次方程。


實際上dp[0][1]即為x[1],dp[0][2]即為x[2]。。。依次類推。

         題目地址:Game

AC程式碼:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
using namespace std;

#define maxn 102
#define eps 1e-10
double g[maxn][maxn];
double x[maxn];
int n,m,k;

void add(int cnt,int i,int j,double val)
{
    int t=i*n+j;
    if(i==m)
    {
        if(j==1)    //p[m][1]=1;結束
            g[cnt][m*n+1]+=-1.0*val;  //方程的右邊
        return;
    }
    g[cnt][t]+=val;
}

void gauss(int n,int m)
{
    int row,col,i,j,k;
    for(row=1,col=1;row<n,col<m;row++,col++)
    {
        k=row;
        for(i=row+1;i<=n;i++)  //列主元
            if(fabs(g[i][col])>fabs(g[k][col]))
                k=i;
        if(k!=row)   //行交換
        {
            for(i=col; i<=m; i++)
                swap(g[k][i],g[row][i]);
        }
        
        for(i=row+1; i<=n; i++)  //主元不是0把下面的行第一個值全部變為0
        {
            if(fabs(g[i][col])<eps)
                continue;
            double t=g[i][col]/g[row][col];
            g[i][col]=0.0;
            for(j=col+1;j<=m;j++)
                g[i][j]-=t*g[row][j];
        }
    }

    for(i=n;i>=1;i--)  //回代求解
    {
        x[i]=g[i][m];
        for(j=i+1;j<=n;j++)
            x[i]-=x[j]*g[i][j];
        x[i]/=g[i][i];
    }
}

int main()
{
    int i,j,cs,nn=0;
    scanf("%d",&cs);
    while(cs--){
        scanf("%d%d%d",&n,&m,&k);
        memset(g,0,sizeof(g));
        int cnt=0;
        for(i=0;i<m;i++)  //i==m的時候只能在右邊出現
            for(j=1;j<=n;j++)
            {
                cnt++;
                add(cnt,i,j,1.0);
                if(j==1)
                {
                    add(cnt,i+1,j,-0.25);
                    add(cnt,1,n-2,-0.75);
                }
                else if(j==2)
                {
                    add(cnt,i+1,n-2,-0.25);
                    add(cnt,1,1,-0.25);
                    add(cnt,1,n-1,-0.5);
                }
                else if(j==3)
                {
                    add(cnt,i+1,n-1,-0.25);
                    add(cnt,1,1,-0.25);
                    add(cnt,1,n-1,-0.25);
                    add(cnt,1,n,-0.25);
                }
                else if(j==4)
                {
                    add(cnt,i+1,n,-0.25);
                    add(cnt,1,n,-0.5);
                    add(cnt,1,1,-0.25);
                }
                else
                {
                    add(cnt,i+1,j-3,-0.25);
                    add(cnt,1,j-3,-0.75);
                }
            }
        gauss(cnt,cnt+1);
        printf("Case #%d: %.6lf\n",++nn,x[k]);
    }
    return 0;
}


相關文章