挑战编程:抛硬币赌博游戏【转】
来源:互联网 发布:淘宝客服忙不忙 编辑:程序博客网 时间: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
- 挑战编程:抛硬币赌博游戏【转】
- 抛硬币的赌博游戏——庞果英雄会
- 赌博游戏
- 赌博游戏
- 赌博游戏
- 编程挑战之罐子和硬币思路
- 抛硬币游戏模拟
- 10 craps赌博游戏
- JAVA赌博游戏
- Craps 赌博游戏
- Grapse赌博游戏
- craps赌博游戏
- 谷神的赌博游戏
- 赌博游戏 概率dp
- [转]中国股市有史以来最大的一场游戏,天字第一号赌博
- 硬币游戏
- 硬币游戏
- 硬币游戏
- 源码搜
- iOS文件管理 NSFileManager NSFileHandle
- Ubuntu中apt-get命令详解
- MPLab X 配置字的设置
- Linux内核关机重启源码分析
- 挑战编程:抛硬币赌博游戏【转】
- iBatis调用返回两个游标的存储过程处理方法
- TwoSat
- apple相关网址
- SQLite 第三版中的数据类型
- < Linux Kernel > Reference Counts
- 如何在 ESRI ArcMap 中打开谷歌卫星地图
- C++小结1
- CentOS 6 修改yum源为本地光盘