有个人有两个0分账号,他可以打比赛,他每次用分低的那个账号打比赛,
有p的概率名列前茅,加50分(上限1000分)
否则掉100分(下限0分)。问有一个账号到1000分的期望。
先压缩一下状态,把 50 50 50看作 1 1 1,把 100 100 100看作 2 2 2,把 1000 1000 1000看作 20 20 20
那么就是一个 d p [ 20 ] [ 20 ] dp[20][20] dp[20][20]的方程
表示最高分是 i i i,次高分是 j j j的期望步数
d p [ i ] [ j ] = d p [ i i ] [ j j ] ∗ p + d p [ i i i ] [ j j j ] ∗ ( 1 − p ) + 1 dp[i][j]=dp[ii][jj]*p+dp[iii][jjj]*(1-p)+1 dp[i][j]=dp[ii][jj]∗p+dp[iii][jjj]∗(1−p)+1
其中 i i , j j ii,jj ii,jj是赢得比赛加分后的状态, i i i , j j j iii,jjj iii,jjj是输掉比赛…
但是 j j j依赖前面比 j j j小的和后面比 j j j大的
这样就直接上高斯消元了.
#include <bits/stdc++.h>
using namespace std;
int id[509][509];
double mp[509][509],ans[509],p;
void add(int i,int j,double w){
mp[i][j]+=w;
}
void guess(int n)//第n列是等式的右边
{
for(int i=1;i<=n;i++)//现在开始消除i列
{
int r=i;//记录系数最大的
for(int j=i+1;j<=n;j++)
if( fabs( mp[r][i] )<fabs( mp[j][i] ) ) r=j;
if( i!=r ) swap( mp[i],mp[r] );//把系数最大的换到当前行
double div = mp[i][i];
for(int j=i;j<=n+1;j++) mp[i][j]/=div;
for(int j=i+1;j<=n;j++)//开始消元
{
div = mp[j][i];
for(int k=i;k<=n+1;k++)
mp[j][k]-=mp[i][k]*div;
}
}
ans[n] = mp[n][n+1];
for(int i=n-1;i>=1;i--)
{
ans[i]=mp[i][n+1];
for(int j=i+1;j<=n;j++)
ans[i]-=mp[i][j]*ans[j];//i之后的解出来了,那么减掉
}
}
int main()
{
int num=0;
for(int i=0;i<=20;i++)
for(int j=0;j<=i;j++)
id[i][j] = ++num;
while( cin >> p )
{
memset(mp,0,sizeof(mp));
for(int i=0;i<=20;i++)
add( id[20][i],id[20][i],-1.0 );
for(int i=0;i<20;i++)
for(int j=0;j<=i;j++)
{
add( id[i][j],id[i][j],-1.0 );
int up = min(20,j+1);
int down = max(0,j-2);
int q = min(i,up), w = max(i,up);
add( id[i][j],id[w][q],p );
q = min(i,down), w = max(i,down);
add( id[i][j],id[w][q],1.0-p );
add( id[i][j],num+1,-1.0 );
}
guess(num);
printf("%.6lf\n",ans[1] );
}
}