题目描述:
小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