挑战编程:抛硬币赌博游戏【转】

来源:互联网 发布:淘宝客服忙不忙 编辑:程序博客网 时间:2024/05/01 14:22


题目描述

小a和小b起初分别有A块钱和B块钱,它们决定玩一个赌博游戏,游戏规则是扔一个硬币,
如果结果是正面的话,小a要给小b C块钱。 否则是反面的话,小b给小a D块钱。
它们不断地扔硬币,直到某一次应该给钱的人拿不出那么多钱,就认为他破产输掉了。
硬币不是均匀的,它以p1的概率产生正面,1 - p1的概率产生反面。
请问小a最后胜利(也就是说小b破产输掉)的概率有多大?
输入:A,B,C,D是整数,0<=A,B<50,0<=C,D<= 100,p1是浮点数 0<=p1<=1;
输出:为保证输出的是整数,请输出小a获胜的概率乘以100后再下取整(直接截断后面的位数)的结果。
例如,结果是0.125则输出12

函数头部 int win(int A,int B,int C,int D,double p1);


题目的意思实际上是玩无限轮小a最后胜利的概率。这个概率由当前两人的钱数和输钱要给对方多少已经确定了。状态转移是比较好想的,但是这个转移是在图上是无向的(也就导致可以玩无限轮),要是直接算你会发现在无限递归。。。

有两种方法可以解决这个问题。


解法一:
引入轮数k,p[n][k]表示小a最初有n块钱,在k轮之内赢的概率。事实上,在k比较大时,p[n][k]收敛到在无限轮内赢的概率。
状态转移比较直接:如果n>=C,那么第一轮可以先输,后面k-1轮赢回来,这个概率是p1*p[n-C][k-1]。反之,只能第一轮就从小b那拿钱了,这个概率是(1-p1)* p[i+D][k-1](注意i+D>A+B时p[i+D][k-1]=1)。

这样很容易写出代码了,轮数取1万应该差不多,时间复杂度是o((A+B)*轮数)。空间复杂度是o(A+B),因为k轮只和k-1轮有关,可以用滚动数组。


#include <cstdio>#include<iostream>#include<cmath>#include<algorithm>using namespace std;const int M=105;const double EPS=1e-7;double p[M][11000];int win(int A,int B,int C,int D,double p1){   int i,k;   for(i=0;i<=A+B;i++) if(i+D>A+B) p[i][1]=1-p1;   else p[i][1]=0;   for(k=2;k<=10000;k++)   for(i=0;i<=A+B;i++)   {       p[i][k]=0;       if(i-C>=0) p[i][k]+=p1*p[i-C][k-1];       p[i][k]+=(1-p1)*((i+D>A+B)?1:p[i+D][k-1]);   }   if(fabs(p[A][k-1]-0.5)<EPS) return 50;   return int(p[A][k-1]*100);}


解法二:

我们还是直接写出和轮数无关的状态转移方程,p[i]表示小a最初有i块钱时赢的概率,用矢量P 表示(p[0],p[1],…,p[A+B])。状态转移方程写出来是P=T*P+Q的形式,我们直接解方程求出P,也就得到了p[A]。复杂度是o((A+B)^3),即高斯消去法的复杂度。这个方法在数学上是精确的,即求出的是无限轮的概率,而不是解法一求出的实际是有限轮的概率。


#include <cstdio>#include<iostream>#include<cmath>#include<algorithm>#include<cstdlib>using namespace std;const int M=105;const double EPS=1e-7;int win(int A,int B,int C,int D,double p1){   int n;  double a[M][M],b[M];     int flag,k,i,j,is;    double d,t;    int js[M];    n=A+B+1;    for(i=0;i<n;i++)    {     for(j=0;j<n;j++) a[i][j]=(i==j)?1.0:0.0;     b[i]=0;    }    for(i=0;i<n;i++)    {        if(i>=C) a[i][i-C]-=p1;        if(i+D<=A+B) a[i][i+D]-=1-p1;        else b[i]=1-p1;    }    flag=1;    for(k=0;k<=n-2;k++)      {          d=0.0;        for(i=k;i<=n-1;i++)          for(j=k;j<=n-1;j++)            {                t=fabs(a[i][j]);              if(t>d) //找最大主元              {                  d=t;                  js[k]=j;                  is=i;              }            }              if(js[k]!=k) //列变换              for(i=0;i<=n-1;i++)                  swap(a[i][k],a[i][js[k]]);              if(is!=k)              {                  //行变换                  for (j=k;j<=n-1;j++)                      swap(a[is][j],a[k][j]);                  swap(b[is],b[k]);              }        d=a[k][k];        for(j=n-1;j>=k;j--) //归一化            a[k][j]/=d;        b[k]/=d;        //消元        for(i=k+1;i<=n-1;i++)          {              t=a[i][k];              for(j=k;j<n;j++)                a[i][j]-=t*a[k][j];            b[i]-=t*b[k];          }      }    d=a[n-1][n-1];    b[n-1]/=d;    for(i=n-2;i>=0;i--)      {          t=0.0;        for(j=i+1;j<=n-1;j++)          t+=a[i][j]*b[j];        b[i]-=t;      }    js[n-1]=n-1;    for(k=n-1;k>=0;k--)      if(js[k]!=k) swap(b[k],b[js[k]]);    if(fabs(b[A]-0.5)<EPS) return 50;    return (int)(b[A]*100);}

转载自: http://blog.csdn.net/shuyechengying/article/details/9822799

原创粉丝点击