HDU 4870 Rating
题意:一个人注册两个账号,初始rating都是0,他每次拿低分的那个号去打比赛,赢了加50分,输了扣100分,胜率为p,他会打到直到一个号有1000分为止,问比赛场次的期望
思路:f(i, j)表示i >= j,第一个号i分,第二个号j分时候,达到目标的期望,那么可以列出转移为f(i, j) = p f(i', j') + (1 - p) f(i'' + j'') + 1
f(i', j')对应的是赢了加分的状态,f(i'', j'')对应输的扣分的状态,可以把50分当作一个单位,一共有20 * 21 / 2 = 210个状态,也就是对应了210个方程组,利用高斯消元去求解方程组,解出f(0, 0)就是答案
代码:
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
const double eps = 1e-9;
const int N = 305;
double p, a[N][N];
int mark[25][25];
double solve() {
for (int i = 0; i < 210; i ++) {
int k = i;
for (; k < 210; k++)
if (fabs(a[k][i]) > eps) break;
for (int j = 0; j <= 210; j++)
swap(a[i][j], a[k][j]);
for (int j = 0; j < 210; j++) {
if (i == j) continue;
if (fabs(a[j][i]) > eps) {
double x = a[j][i] / a[i][i];
for (int k = i; k <= 210; k++) {
a[j][k] -= a[i][k] * x;
}
}
}
}
return a[0][210] / a[0][0];
}
void build() {
memset(a, 0, sizeof(a));
for (int i = 0; i < 20; i++) {
for (int j = 0; j < i; j++) {
int u = mark[i][j];
a[u][u] = 1;
a[u][210] = 1;
int v = mark[i][max(0, j - 2)];
a[u][v] -= (1 - p);
v = mark[i][j + 1];
a[u][v] -= p;
}
int u = mark[i][i];
a[u][u] = 1;
a[u][210] = 1;
int v = mark[i][max(0, i - 2)];
a[u][v] -= (1 - p);
v = mark[i + 1][i];
a[u][v] -= p;
}
}
int main() {
int cnt = 0;
memset(mark, -1, sizeof(mark));
for (int i = 0; i < 20; i++) {
for (int j = 0; j <= i; j++) {
mark[i][j] = cnt;
cnt++;
}
}
while (~scanf("%lf", &p)) {
build();
printf("%.6lf\n", solve());
}
return 0;
}