概率dp+高斯消元解方程组-hdu-4326-Game

题目链接:

http://acm.hdu.edu.cn/showproblem.php?pid=4326

题目大意:

有n个人,每次前面四个人玩游戏,每人赢得概率为1/4,四人中赢的人排在队首,输的人依次排到队尾。求第k个人连续赢m次的概率。

解题思路:

之前做过,每次博客,现在补上。

因为要考虑连续赢的情况并且每次动作只和第一个人有关,设第一维表示第一个人连续赢的次数。第二维表示第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[i][j]顺序作为x1,x2,x3,,,,,,,,,,,,,

      ( 0 1)  (0 2)..(0,n)....(m-1,n) 

(0 1)

(0 2)

....

(0,n)

....

(m-1,n)

代码:

#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<cstring>
#include<algorithm>
#include<vector>
#include<map>
#include<stack>
#include<list>
#include<queue>
#define eps 1e-6
#define INF 0x1f1f1f1f
#define PI acos(-1.0)
#define ll __int64
#define lson l,m,(rt<<1)
#define rson m+1,r,(rt<<1)|1
using namespace std;

/*
freopen("data.in","r",stdin);
freopen("data.out","w",stdout);
*/
#define Maxn 120
double g[Maxn][Maxn];
double x[Maxn];
int n,m,k;

void add(int row,int i,int j,double v)
{
   int col=i*n+j;

   if(i==m)
   {
      if(j==1) //p[i][j]=1; 为常数
         g[row][m*n+1]+=-1.0*v;
      return ;
   }
   g[row][col]+=v;
   return ;
}
void gg(int r,int c) //高斯消元 求解方程组
{
   int row,col;

   for(row=1,col=1;row<r,col<c;row++,col++)
   {
      int t=row;
      for(int i=row+1;i<=r;i++)
         if(fabs(g[i][col])>fabs(g[t][col])) //找最大的主元
            t=i;
      if(fabs(g[t][col])<eps) //说明这一列全部为零
         continue;
      if(t-row) //不同则交换
      {
         for(int i=col;i<=c;i++)
            swap(g[row][i],g[t][i]);
      }
      for(int i=row+1;i<=r;i++)
      {
         if(fabs(g[i][col])<eps) //已经是零了
            continue;
         double tt=g[i][col]/g[row][col];
         g[i][col]=0.0; //把后面的系数同一
         for(int j=col+1;j<=c;j++)
            g[i][j]-=tt*g[row][j];
      }
   }
   for(int i=r;i>=1;i--) //逆着往前求
   {
      x[i]=g[i][c];
      for(int j=r;j>i;j--)
         x[i]-=g[i][j]*x[j];
      x[i]/=g[i][i];
   }
   return ;

}

int main()
{
   int t;

   scanf("%d",&t);
   for(int ca=1;ca<=t;ca++)
   {
      memset(g,0,sizeof(g));
      scanf("%d%d%d",&n,&m,&k);
      int row=0;
      for(int i=0;i<m;i++) //这里的i=0表示
         for(int j=1;j<=n;j++)
         {
            row++; //表示每一个方程 第0行和第0列都没用
            add(row,i,j,1.0); //加入到矩阵中
            if(j==1)
            {
               add(row,i+1,j,-0.25); //移项后加入到矩阵中
               add(row,1,n-2,-0.75);
            }
            else if(j==2)
            {
                add(row,i+1,n-2,-0.25);
                add(row,1,1,-0.25);
                add(row,1,n-1,-0.5);
            }
            else if(j==3)
            {
               add(row,i+1,n-1,-0.25);
               add(row,1,n-1,-0.25);
               add(row,1,1,-0.25);
               add(row,1,n,-0.25);
            }
            else if(j==4)
            {
               add(row,i+1,n,-0.25);
               add(row,1,n,-0.5);
               add(row,1,1,-0.25);
            }
            else
            {
               add(row,i+1,j-3,-0.25);
               add(row,1,j-3,-0.75);
            }
         }
      gg(row,n*m+1); //高斯消元求解m*n元方程
      printf("Case #%d: %.6lf\n",ca,x[k]);
   }
   return 0;
}






你可能感兴趣的:(动态规划)