1.000000 0.814700
39.000000 82.181160
题意:
一个人打cf。规则是如果他排名前200就加50分最高加到1000.否侧减100分。最低到0分。现在告诉你他一场比赛前200的概率p.然后他申请了两个账号初始分都为0.每次比赛他会用分数低的那个账号低的那个账号打。现在问你他要上1000.有一个账号上就行了。需要参加比赛场数的期望。
思路:
由于加减分都是50的倍数。所以分数可以用[0,20]表示。没次可以减2分或加1分。用dp[i][j]表示分数高的账号分数为i。分数低的账号分数为j然后有一个账号上1000的概率。
那么dp[i][j]=p*(dp[i][j+1]+1)+(1-p)*(dp[i][j-2]+1) i>j。如果j+1大于i就换成dp[j+1][i]。如果j-2小于0就换成dp[i][0]。然后对每个dp[i][j]编号。建立方程。然后高斯消元。
详细见代码:
#include<algorithm> #include<iostream> #include<string.h> #include<stdio.h> #include<math.h> using namespace std; const int INF=0x3f3f3f3f; const double eps=1e-11; const int maxn=100010; typedef long long ll; int cnt,mp[50][50]; double mat[310][310]; bool gauss() { int row,i,j,id; double maxx,var; for(row=0;row<cnt;row++) { maxx=fabs(mat[row][row]); id=row; for(i=row+1;i<cnt;i++)//mat[i][cnt]为常数项 { if(fabs(mat[i][row])>maxx) { maxx=fabs(mat[i][row]); id=i; } } if(maxx<eps) return false; if(id!=row) { for(i=row;i<=cnt;i++) swap(mat[row][i],mat[id][i]); } for(i=row+1;i<cnt;i++) { if(fabs(mat[i][row])<eps) continue; var=mat[i][row]/mat[row][row]; for(j=row;j<=cnt;j++) mat[i][j]-=mat[row][j]*var; } } for(i=cnt-1;i>=0;i--) { for(j=i+1;j<cnt;j++) mat[i][cnt]-=mat[i][j]*mat[j][j]; mat[i][i]=mat[i][cnt]/mat[i][i]; } return true; } int main() { int i,j,ptr,base,pp,a,b,c; double p; for(i=0;i<=20;i++) for(j=0,base=i*(i+1)/2;j<=i;j++) mp[i][j]=base+j; cnt=231; while(~scanf("%lf",&p)) { ptr=0; memset(mat,0,sizeof mat); for(i=0;i<=20;i++) { for(j=0;j<=i;j++) { if(i==20) { pp=mp[i][j]; mat[ptr][pp]=1; mat[ptr++][cnt]=0; continue; } a=max(i,j+1); b=min(i,j+1); c=max(0,j-2); mat[ptr][cnt]=1; pp=mp[i][j]; mat[ptr][pp]+=1; pp=mp[a][b]; mat[ptr][pp]+=-p; pp=mp[i][c]; mat[ptr++][pp]+=p-1; } } gauss(); printf("%.8lf\n",mat[0][0]); } return 0; }