有个人有两个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](1p)+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] );
	}
}