HDU 3076 ssworld VS DDD
题意: 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