一个人用两个帐号参加比赛,每次他都用积分低的那个参加,参加一场比赛有p的概率让那个号的积分加一,有(1-p)的概率让那个号的积分减二。最开始两个号的积分都是0,问他有一个号的积分达到20的期望比赛次数。
定义状态dp[k]为只使用一个帐号,从积分为k到积分为k+1的期望比赛次数,则dp[k]=1+p*0+(1-p)*(dp[k-2]+dp[k-1]+dp[k]),k>=2,dp[1]=1+p*0+(1-p)*(dp[0]+dp[1]),dp[0]=1+p*0+(1-p)*(dp[0])。
因为他使用一个帐号比赛,如果输了就会一直用这个帐号,直到积分涨回去比原来还高为止。
所以ans=dp[0]*2+dp[1]*2+...+dp[18]*2+dp[19]
#include <cstdio>
#include <cstring>
#include <cmath>
double dp[20];
double p;
int main() {
int i,j;
double ans;
while (scanf("%lf",&p)!=EOF) {
dp[0]=1/p;
dp[1]=(1+(1-p)*dp[0])/p;
for (i=2;i<20;i++) {
dp[i]=(1+(1-p)*(dp[i-2]+dp[i-1]))/p;
}
ans=dp[19];
for (i=0;i<19;i++) {
ans+=dp[i]+dp[i];
}
printf("%.8lf\n",ans);
}
return 0;
}
附上一个过不去的代码..
定义状态dp[i][j]表示两个号分别有i和j的积分,其中i<=j。带入消元法。
事实证明因为除法次数太多,所以丢精度丢的太厉害...所以跪了...
#include <cstdio>
#include <cstring>
long double p,p1;
long double dp[201][201];
long double a[201],b[201],c[201];
long double x,y;
void calxy(long double a3,long double b3,long double c3,long double a4,long double b4,long double c4,long double a5,long double b5,long double c5) {
long double a1=a3-a4,a2=a3-a5;
long double b1=b3-b4,b2=b3-b5;
long double c1=c3-c4,c2=c3-c5;
//printf("%Lf %Lf\n",a1*b2-a2*b1,b1*a2-b2*a1);
x=(c2*b1-c1*b2)/(a1*b2-a2*b1);
y=(c2*a1-c1*a2)/(b1*a2-b2*a1);
}
int main() {
int i,j;
for (i=0;i<=20;i++) {
dp[i][20]=0;
}
while (scanf("%Lf",&p)!=EOF) {
if (p==1) printf("39.000000\n");
else {
p1=1-p;
for (j=19;j>=2;j--) {
a[j]=0;b[j]=1;c[j]=0;
a[j-1]=1;b[j-1]=0;c[j-1]=0;
a[j-2]=0;b[j-2]=1/p1;c[j-2]=(-1-p*dp[j][j+1])/p1;
for (i=j-3;i>=0;i--) {
a[i]=(a[i+2]-p*a[i+3])/p1;
b[i]=(b[i+2]-p*b[i+3])/p1;
c[i]=(c[i+2]-p*c[i+3]-1)/p1;
}
calxy(a[0],b[0],c[0],(a[1]-p*a[2])/p1,(b[1]-p*b[2])/p1,(c[1]-p*c[2]-1)/p1,a[1],b[1],c[1]+1/p);
dp[j][j]=y;
dp[j-1][j]=x;
dp[j-2][j]=(dp[j][j]-1-p*dp[j][j+1])/p1;
for (i=j-3;i>=0;i--) {
dp[i][j]=(dp[i+2][j]-1-p*dp[i+3][j])/p1;
}
}
dp[1][1]=1/p+dp[1][2]+p1/p/p;
dp[0][1]=1/p+dp[1][1];
dp[0][0]=1/p+dp[0][1];
printf("%.8Lf\n",dp[0][0]);
}
}
return 0;
}