题意: 2个人分别有AB的血数,轮流扔骰子,数小的自减一血,平的不变,谁先到减0, 谁输,问A赢的概率。
题解: 考虑平局的出现对局面没有影响,因此把平局规约到非平局里即可,对于每一次p1表示A赢,p2表示B赢,p=1-p1-p2表示平局,A赢的概率为p1+p*p1+p^2*p1+…p^n*p1,n->无穷,即a=q1/(1-p);b=q2/(1-p);
然后在他们一定会分出胜负的情况下就可以dp了:
dp[i][j]=dp[i][j-1]*a+dp[i-1][j]*b;
代码很丑,数组开的太大还MLE一次
#include <cstdio> #include <cstring> const int maxn=2000+5; double sum[2][7]; double p[2][7] , dp[maxn][maxn]; int A,B; double a,b;//算上平局的单次 double a1,b1;//单次 void DP() { memset (dp, 0, sizeof(dp)); a=b=a1=b1=0.0; for (int i=1 ; i<6 ; ++i) { a1+=sum[1][i]*p[0][i+1]; b1+=sum[0][i]*p[1][i+1]; } //printf("a==%lf , b==%lf\n", a1, b1); double p=1-a1-b1; a=a1/(1.0-p); b=b1/(1.0-p); //printf("%lf , %lf , 平局%lf\n", a, b, p); dp[0][0]=1; for (int i=0 ; i<=A ; ++i) { for (int j=0 ; j<=B ; ++j) { if(i>0 && j<B)dp[i][j]+=dp[i-1][j]*a; if(j>0 && i<A)dp[i][j]+=dp[i][j-1]*b; } //printf("\n"); } double ans=0; for (int i=0 ; i<B ; ++i) { ans+=dp[A][i]; } printf("%lf\n",ans); } /* 5 5 0.500 0.500 0.000 0.000 0.000 0.000 0.000 0.000 0.000 0.000 0.000 1.000 5 5 0.000 0.000 0.000 0.000 0.900 0.100 0.000 0.000 0.000 0.000 0.900 0.100 */ int main () { while (~scanf("%d%d",&A,&B)) { sum[0][0]=sum[1][0]=0; for (int i=1 ; i<=6 ; ++i) { scanf("%lf", p[0]+i); sum[0][i]=sum[0][i-1]+p[0][i]; } for (int i=1 ; i<=6 ; ++i) { scanf("%lf", p[1]+i); sum[1][i]=sum[1][i-1]+p[1][i]; } DP(); } return 0; }【------------------------------------------------------】
m道题,t个队伍,n个最少题数,给出t*m的概率,表示队伍做出题的概率。求所有队伍都至少出1题,且至少有一个队伍出n道题以上概率。
#include <cstdio> #include <cstring> double dp[35][35]; double sum[3]; int main () { int m,t,n; double p; while (~scanf("%d%d%d", &m, &t, &n) , (m||n||t)) { double ans=1.0,ans2=1.0; for (int i=0 ; i<t ; ++i) { dp[0][0]=1; for (int j=1 ; j<=m ; ++j) { scanf("%lf", &p); for (int k=0 ; k<=j && k<=m ; ++k) dp[k][j]=dp[k][j-1]*(1-p)+(k?dp[k-1][j-1]*p:0); } sum[0]=dp[0][m]; sum[1]=0.0; for (int j=1 ; j<n ; ++j)sum[1]+=dp[j][m]; ans*=(1-sum[0]); ans2*=sum[1]; } printf("%.3lf\n",ans-ans2); } return 0; }
【---------------------------------------------------】
HDU 3853 (2011夏)北邮邀请赛 i题
错误代码:
正向推的公式是错的, 原因是用错了无穷级数。
#include <cstdio> #include <cstring> const int maxn=1000+12; const double inf=20000000.0; using namespace std; double map[maxn][maxn][3], dp[maxn][maxn]; ///dp[i][j]=(dp[i-1][j]+1)*map[i-1][j][2]+(dp[i][j-1]+1)*map[i][j-1]+(dp[i][j]+1)*map[i][j] int n, m; double tmp; int main ( ) { while (~scanf("%d%d", &n, &m)) { dp[1][1]=0.; for (int i=1 ; i<=n ; ++i) { for (int j=1 ; j<=m ; ++j) { tmp=0.0; scanf("%lf%lf%lf", map[i][j]+0, map[i][j]+1, map[i][j]+2); if(map[i][j][0]!=1.0) { if(i)tmp+=(dp[i-1][j]+1)*map[i-1][j][2]; if(j)tmp+=(dp[i][j-1]+1)*map[i][j-1][1]; tmp+=map[i][j][0]; tmp/=(1-map[i][j][0]); } else tmp=0.; dp[i][j]=tmp; ///printf("%d %d %lf\n", i, j, dp[i][j]); } } dp[n][m]=map[n-1][m][2]*(dp[n-1][m]+1)+map[n][m-1][1]*(dp[n][m-1]+1); printf("%.3lf\n", dp[n][m]*2); } return 0; } /* 2 2 0.00 0.50 0.50 0.50 0.00 0.50 0.50 0.50 0.00 1.00 0.00 0.00 3 2 0.00 0.50 0.50 0.50 0.00 0.50 0.00 0.50 0.50 0.50 0.00 0.50 0.50 0.50 0.00 1.00 0.00 0.00 */
改成向前推就好了。
在2维状态下递推期望 , 利用期望的公式,
E = x1 * p1 + x2 * p2 + …… xn*pn。
状态转移方程式:
f[i][j] =(f[i][j]+2)* exp[i][j][1] + (f[i+1][j]+2) * exp[i][j][2] + (f[i][j+1]+2) * exp[i][j][3]
这个式子左右都有f[i][j],化简后:
f[i][j] =((f[i+1][j]+2) * exp[i][j][2] + (f[i][j+1]+2) * exp[i][j][3]+ 2 * exp[i][j][1])/ (1 - exp[i][j][1])
#include <cstdio> #include <cstring> const int maxn=1000+12; using namespace std; double map[maxn][maxn][3], dp[maxn][maxn]; double tmp; int main () { while (~scanf("%d%d", &n, &m)) { for (int i=1 ; i<=n ; ++i) { for (int j=1 ; j<=m ; ++j) { scanf("%lf%lf%lf", map[i][j]+0, map[i][j]+1, map[i][j]+2); } } memset (dp, 0, sizeof(dp)); for (int i=n ; i>0 ; --i) { for (int j=m ; j>0 ; --j) { if(i==n && j==m)continue; if(map[i][j][0]!=1.) { tmp=1+dp[i][j+1]*map[i][j][1]+dp[i+1][j]*map[i][j][2]; tmp/=(1-map[i][j][0]); } dp[i][j]=tmp; } } printf("%.3lf\n", dp[1][1]*2); } return 0; }
HDU 4050 /// bupt 现场赛 I题 POJ3071 1202