Hdu 4870 Rating(概率期望)

A little girl loves programming competition very much. Recently, she has found a new kind of programming competition named “TopTopTopCoder”. Every user who has registered in “TopTopTopCoder” system will have a rating, and the initial value of rating equals to zero. After the user participates in the contest held by “TopTopTopCoder”, her/his rating will be updated depending on her/his rank. Supposing that her/his current rating is X, if her/his rank is between on 1-200 after contest, her/his rating will be min(X+50,1000). Her/His rating will be max(X-100,0) otherwise. To reach 1000 points as soon as possible, this little girl registered two accounts. She uses the account with less rating in each contest. The possibility of her rank between on 1 - 200 is P for every contest. Can you tell her how many contests she needs to participate in to make one of her account ratings reach 1000 points?
Input
There are several test cases. Each test case is a single line containing a float number P (0.3 <= P <= 1.0). The meaning of P is described above.
Output
You should output a float number for each test case, indicating the expected count of contest she needs to participate in. This problem is special judged. The relative error less than 1e-5 will be accepted.
Sample Input
1.000000
0.814700
Sample Output
39.000000
82.181160

题意:

一个人拿了两个账号去打比赛,初始都0分,每次比赛用分数低的账号。每次比赛上分概率p。一次不是上1分就是掉2分,求其中一个账号达到20分的期望步数。

思路:

首先,两个账号的条件是不用管的,最终状态肯定是一个19分,一个20分。

然后DP方程也很好写,设 f [ i ] f[i] 表示从i分上到20分的期望步数,那么很容易得到

f [ i ] = f [ m i n ( n , i + 1 ) ] p + f [ m a x ( 0 , i 2 ) ] ( 1 p ) + 1 f[i]=f[min(n,i+1)]*p+f[max(0, i-2)]*(1-p)+1

注意的是,如果把 f [ i ] f[i] 的含义定义为从0到i分的期望步数,方程变成

f [ i ] = ( f [ i 1 ] + 1 ) p + ( f [ i + 2 ] + 1 ) ( 1 p ) f[i]=(f[i-1]+1)*p+(f[i+2]+1)*(1-p)
f [ 0 ] = ( f [ 1 ] + 1 + [ 2 ] + 1 ) p , f [ 20 ] = ( f [ 19 ] + 1 ) ( 1 p ) f[0]=(f[1]+1+[2]+1)*p,f[20]=(f[19]+1)*(1-p)

那么更新就会出非常玄学的错误。虽然看起来非常正确,但是对于f[0]和f[20],用于更新的概率之和不为1,得到的结果也是错的。概率期望的DP方程最好写逆推,从最终达到就会停止操作的状态开始推。(以后找到正推方程再更新)
因为这个方程有后效性,不能直接DP,所以用高斯消元做。

或者,转换一下思路, f [ i ] f[i] 表示从i上1分的期望步数,那么
f [ i ] = 1 p + ( 1 p ) ( 1 + f [ i 2 ] + f [ i 1 ] + f [ i ] ) f[i] = 1*p+(1-p)*(1+f[i-2]+f[i-1]+f[i])
由上面那种方法的DP方程略作改变也可以得到同样的式子,因为(g[i]指代上面的f[i])
f [ i ] = d e l t a g = g [ i + 1 ] g [ i ] f[i]=delta_g=g[i+1]-g[i]
化简之后
f [ i ] = ( 1 + ( 1 p ) ( f [ i 2 ] + f [ i 1 ] ) ) / p f[i]=(1+(1-p)*(f[i-2]+f[i-1]))/p
初始化
f [ 0 ] = 1 p + ( 1 p ) ( 1 + f [ 0 ] ) , f [ 1 ] = 1 p + ( 1 p ) ( 1 + f [ 0 ] + f [ 1 ] ) f[0]=1*p+(1-p)*(1+f[0]),f[1]=1*p+(1-p)*(1+f[0]+f[1])
化简为
f [ 0 ] = 1 / p , f [ 1 ] = 1 / p + ( 1 p ) / p 2 f[0]=1/p,f[1]=1/p+(1-p)/p^2

于是就可以不用打高斯消元了!!!代码不知道短到哪里去了!!!

不知道短到哪里去了!!!

不知道短到哪里去了!!!

哇哈哈哈哈哈哈!!!

两种代码都贴上来:

/*
Gauss Elimination
*/
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
using namespace std;
const double eps = 1e-6;
const int n = 20;
double p, a[n+10][n+10];

void test()
{
	for (int i = 0; i <= n; i++){
		for (int j = 0; j <= n+1; j++)
			if (abs(a[i][j]) < eps)
				cout << 0 << " ";
			else printf("%.2f ", a[i][j]);
		cout << endl;
	}
	cout << endl;
}

void InitializeMatrix()
{
	memset(a, 0, sizeof(a));
	for (int i = 0; i < n; i++){
		a[i][i] += 1;
		a[i][n+1] += 1;
		a[i][max(0, i-2)] += p-1;
		a[i][min(n, i+1)] += -p;
	}
	a[n][n] = 1; a[n][n+1] = 0;
//	test();
}

inline void Swap(double &x, double &y)
{
	double tmp = x;
	x = y;
	y = tmp;
}

void GaussElimination()
{
	for (int i = 0; i <= n; i++){
		int row = i;
		for (int j = i+1; j <= n; j++)
			if (a[j][i] > a[row][i])
				row = j;
		if (abs(a[row][i]) < eps)
			continue;
		if (row != i)
			for (int j = i; j <= n+1; j++)
				Swap(a[row][j], a[i][j]);
		for (int j = n+1; j >= i; j--)
			a[i][j] /= a[i][i];
		for (int j = 0; j <= n; j++)
			if (j != i)
				for (int k = n+1; k >= i; k--)
					a[j][k] -= a[i][k]*a[j][i];
//		test();
//		char c = getchar();
	}
//	test();
	printf("%.6f\n", 2*a[0][n+1]-a[19][n+1]);
}

int main()
{
	while (scanf("%lf", &p) == 1){
		InitializeMatrix();
		GaussElimination();
	}
	return 0;
}

/*
short shorter and shortest
*/
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N = 50;
double p, f[N], sum[N];

int main()
{
	while (scanf("%lf", &p) == 1){
		f[0] = 1/p;
		f[1] = 1/p+(1-p)/(p*p);
		sum[2] = f[0]+f[1];
		for (int i = 2; i <= 19; i++){
			f[i] = (1+(1-p)*(f[i-1]+f[i-2]))/p;
			sum[i+1] = sum[i]+f[i];
		}
		printf("%.6f\n", sum[20]+sum[19]);
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/xyyxyyx/article/details/82883963