给出 1 1 1到 100 100 100共 100 100 100个格子排成一行, 到达一些格子时会瞬间转移, 每次投骰子来确定走的步数, 如果走出格子之外就重新投, 问走到 100 100 100号格子的期望步数.
如果每个格子瞬间转移只会向后的话, 就是普通的期望
d
p
dp
dp—只须从后往前dp即可. 但是有些格子可能会往回跳, 所以不满足无后效性.
按照普通期望
d
p
dp
dp的做法,
d
p
[
i
]
dp[i]
dp[i]表示从
i
i
i这里开始走, 走到终点的期望步数. 可惜在当前情景下, 我们计算
i
i
i时可能会用到
1
1
1到
i
−
1
i-1
i−1的
d
p
dp
dp值, 而计算
1
1
1到
i
−
1
i-1
i−1的dp值又依赖于
i
i
i位置的
d
p
dp
dp值, 这样不管我们先算哪个都无从下手, 明显要解方程了…
我们考虑高斯消元来解方程.
那么具体的方程是啥嘞
Ⅰ 最后一个格子走到目标点的期望:
d
p
[
100
]
=
0
dp[100]=0
dp[100]=0;
Ⅱ 如果一个格子会瞬间转移, 那他的期望当然就等于转移到的那个格子的期望:
d
p
[
i
]
=
d
p
[
t
o
[
i
]
]
dp[i]=dp[to[i]]
dp[i]=dp[to[i]];
Ⅲ 剩下的格子当然是没有瞬间转移的, 但可能会在投骰子之后跳出边界.
对于情况Ⅲ, 我举一个例子, 通过这个例子就能够推出公式了.
不妨设最后5个格子分别为
A
B
C
D
E
ABCDE
ABCDE, 而我们现在在
A
A
A上 ,并且
A
A
A上不会瞬间转移. 显然我们投到5或6都会导致重新投,
1234
1234
1234都会直接走向
B
C
D
E
BCDE
BCDE四种情况.
根据期望dp的常规套路, 我们很容易列出这样一个式子:
A
=
1
6
(
B
+
1
)
+
1
6
(
C
+
1
)
+
1
6
(
D
+
1
)
+
1
6
(
E
+
1
)
+
2
6
(
A
+
1
)
A=\frac{1}{6}(B+1)+\frac{1}{6}(C+1)+\frac{1}{6}(D+1)+\frac{1}{6}(E+1)+\frac{2}{6}(A+1)
A=61(B+1)+61(C+1)+61(D+1)+61(E+1)+62(A+1)
简单化简就是这样:
A
=
B
+
C
+
D
+
E
+
6
4
A=\frac{B+C+D+E+6}{4}
A=4B+C+D+E+6
所以对于情况Ⅲ的格子
i
i
i:
d
p
[
i
]
=
∑
b
a
c
k
+
6
c
n
t
dp[i]=\frac{\sum{back}+6}{cnt}
dp[i]=cnt∑back+6, cnt就是后面格子的个数.
高斯消元的话, 先把未知项移到左边, 常数项放等式右边, 第i行第j列对应第i个式子第j个变量的系数, 最后一列放常数项.
代码:
void init() {
int _ = read(), caz = 0;
while (_--) {
int n = read();
for (int i = 1; i <= 100; ++i) {
to[i] = i;
}
for (int i = 1; i <= n; ++i) {
int a = read(), b = read();
to[a] = b;
}
fill(mat[0], mat[0] + 110 * 110, 0);
mat[100][100] = 1;
mat[100][101] = 0;
for (int i = 1; i <= 99; ++i) {
if (to[i] != i) {
mat[i][i] = 1;
mat[i][to[i]] = -1;
mat[i][101] = 0;
} else {
int cnt = 0;
for (int j = 1; j <= 6; ++j) {
if (i + j <= 100) {
cnt++;
mat[i][i + j] = -1;
}
}
mat[i][i] = cnt;
mat[i][101] = 6;
}
}
Gauss(101, 101);
printf("Case %d: %.8lf\n", ++caz, x[1]);
}
}
高斯消元:
int Gauss(int equ, int var) {
int i, j, k;
int max_r;
int col;
int ta, tb;
int LCM;
int temp;
int free_index, free_num;
memset(free_x, 1, sizeof(free_x));
memset(x, 0, sizeof(x));
for (k = col = 0; k < equ && col < var; ++k, ++col) {
max_r = k;
for (int i = k + 1; i < equ; ++i) {
if (fabs(mat[i][col]) - fabs(mat[max_r][col]) > eps) {
max_r = i;
}
}
if (max_r != k) {
for (int j = k; j < var + 1; ++j) {
swap(mat[max_r][j], mat[k][j]);
}
}
if (fabs(mat[k][col]) <= eps) {
--k;
continue;
}
for (int i = k + 1; i < equ; ++i) {
if (fabs(mat[i][col]) <= eps) {
continue;
}
double tmp = mat[i][col] / mat[k][col];
for (int j = col; j < var + 1; ++j) {
mat[i][j] -= mat[k][j] * tmp;
}
}
}
for (int i = k; i < equ; ++i) {
if (fabs(mat[i][var]) > eps) {
return 0;
}
}
if (k < var) {
for (int i = k - 1; i >= 0; --i) {
free_num = 0;
for (int j = 0; j < var; ++j) {
if (fabs(mat[i][j]) > eps && free_x[j]) {
++free_num;
free_index = j;
}
}
if (free_num > 1) {
continue;
}
double tmp = mat[i][var];
for (int j = 0; j < var; ++j) {
if (j != free_index && fabs(mat[i][j]) > eps) {
tmp -= mat[i][j] * x[j];
}
}
free_x[free_index] = 0;
x[free_index] = tmp / mat[i][free_index];
}
return var - k;
}
for (int i = var - 1; i >= 0; --i) {
double tmp = mat[i][var];
for (int j = i + 1; j < var; ++j) {
if (fabs(mat[i][j]) > eps) {
tmp -= x[j] * mat[i][j];
}
}
x[i] = tmp / mat[i][i];
}
return 1;
}