HDU 4326Game(比较难理解的概率dp)
Game
Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 229 Accepted Submission(s): 85
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.
Flowing T line, each line contains 3 integer N, M, K.(4<=N<=10, M<=10,K<=N)
因为要考虑连续赢的情况并且每次动作只和第一个人有关,设第一维表示第一个人连续赢的次数。第二维表示第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元的一次方程。
#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; }