Snakes and Ladders
题目大意: 100 100 100 个格子,我们起始点在 1 1 1 ,每次抛一个六面的骰子,掷到几就往前走几步,这中间会有梯子或者蛇,实际上都是传送门,一个是往前传,一个是往后传,走到 100 100 100 则游戏结束,如果将要走的地方是出界的,那么将重新掷骰子,现在给出 n n n 个传送门的信息 u − > v u->v u−>v ,计算游戏结束时掷骰子的次数期望,题目保证肯定有机会结束游戏。
我们设从 i i i 出发结束游戏的掷骰子的次数期望为 d p [ i ] dp[i] dp[i] ,则我们可以列出下面三种情况的转移方程。
1.若有传送门:
d
p
[
i
]
=
d
p
[
t
o
[
i
]
]
dp[i] = dp[to[i]]
dp[i]=dp[to[i]]
说明:传送门是不需要掷骰子的,所以此位置有传送门肯定就等于传送点的期望。
2.若其不会越界:
d
p
[
i
]
=
∑
j
=
i
+
1
i
+
6
(
1
6
∗
(
d
p
[
j
]
+
1
)
)
=
∑
j
=
i
+
1
i
+
6
(
1
6
∗
d
p
[
j
]
)
+
1
dp[i] = \sum\limits_{j=i+1}^{i+6}(\frac{1}{6}*(dp[j]+1))=\sum\limits_{j=i+1}^{i+6}(\frac{1}{6}*dp[j])+1
dp[i]=j=i+1∑i+6(61∗(dp[j]+1))=j=i+1∑i+6(61∗dp[j])+1
说明:那不会越界的位置的期望就分别由六种情况组成,且这六种情况是等概率的,所以可以列出上式。
3.若其可能越界:
d
p
[
i
]
=
∑
j
=
i
+
1
100
(
1
6
∗
(
d
p
[
j
]
+
1
)
)
+
(
i
+
6
−
100
6
∗
(
d
p
[
i
]
+
1
)
)
dp[i]=\sum\limits_{j=i+1}^{100}(\frac{1}{6}*(dp[j]+1))+(\frac{i+6-100}{6}*(dp[i]+1))
dp[i]=j=i+1∑100(61∗(dp[j]+1))+(6i+6−100∗(dp[i]+1))
也就是
d
p
[
i
]
=
(
i
+
6
−
100
6
∗
d
p
[
i
]
)
+
∑
j
=
i
+
1
100
(
1
6
∗
d
p
[
j
]
)
+
1
dp[i]=(\frac{i+6-100}{6}*dp[i])+\sum\limits_{j=i+1}^{100}(\frac{1}{6}*dp[j])+1
dp[i]=(6i+6−100∗dp[i])+j=i+1∑100(61∗dp[j])+1
说明:分两种情况去考虑,第一种就是界内,另一种就是界外,界内的和上一种情况同理计算。那么界外的期望部分实际上就是:
抛
到
界
外
的
概
率
×
(
1
+
后
续
掷
骰
子
期
望
次
数
)
抛到界外的概率 × (1+后续掷骰子期望次数)
抛到界外的概率×(1+后续掷骰子期望次数) ,后续掷骰子的期望次数不就是相当于重来一次嘛,那么自然就是
d
p
[
i
]
dp[i]
dp[i]
我们发现这里因为存在下行传送门的缘故,无法线性的直接递推过去,但是我们发现我们可以列出 100 100 100 个方程,如下
对于每一个位置 i ( i ≠ 100 ) i(i≠100) i(i=100) ,我们都可以列出一个方程
1.若有传送门: x i − x t o = 0 x_i-x_{to}=0 xi−xto=0
2.若其不会越界( i ≥ 94 i≥94 i≥94 ): x i + ∑ j = i + 1 i + 6 ( − 1 6 ∗ x j ) + 1 = 0 x_i+\sum\limits_{j=i+1}^{i+6}(-\frac{1}{6}*x_{j})+1=0 xi+j=i+1∑i+6(−61∗xj)+1=0
3.若其会越界: 100 − i 6 ∗ x i + ∑ j = i + 1 100 ( − 1 6 ∗ x j ) + 1 = 0 \frac{100-i}{6}*x_i+\sum\limits_{j=i+1}^{100}(-\frac{1}{6}*x_j)+1=0 6100−i∗xi+j=i+1∑100(−61∗xj)+1=0
实际上呢, 2 , 3 2,3 2,3 两点是可以合并的: m i n ( 6 , 100 − i ) 6 ∗ x i + ∑ j = i + 1 m i n ( i + 6 , 100 ) ( − 1 6 ∗ x j ) + 1 = 0 \frac{min(6,100-i)}{6}*x_i+\sum\limits_{j=i+1}^{min(i + 6,100)}(-\frac{1}{6}*x_j)+1=0 6min(6,100−i)∗xi+j=i+1∑min(i+6,100)(−61∗xj)+1=0
于是最终我们就可以得到两种情况:
1.有传送门: x i − x t o = 0 x_i-x_{to}=0 xi−xto=0
2.无传送门: m i n ( 6 , 100 − i ) 6 ∗ x i + ∑ j = i + 1 m i n ( i + 6 , 100 ) ( − 1 6 ∗ x j ) + 1 = 0 \frac{min(6,100-i)}{6}*x_i+\sum\limits_{j=i+1}^{min(i + 6,100)}(-\frac{1}{6}*x_j)+1=0 6min(6,100−i)∗xi+j=i+1∑min(i+6,100)(−61∗xj)+1=0
于是我们就可以列出 100 100 100 (包括已知的 x 100 = 0 x_{100}=0 x100=0 )个方程,按照题中说的,我们是一定可以走到终点的,也就是说,这个方程是一定有解的。
最终我们用高斯消元求解,输出 x 1 x_1 x1 即可
#include <bits/stdc++.h>
using namespace std;
const int N = 1e2 + 10;
const double eps = 1e-10;
int n, to[N], cs;
double g[N][N];
double x[N];
void build() {
for (int i = 1; i < 100; ++i) {
if (~to[i]) {
g[i][i] = 1;
g[i][to[i]] = -1;
}
else {
g[i][i] = 1.0 * (min(6, 100 - i)) / 6;
for (int j = i + 1; j <= min(i + 6, 100); ++j) {
g[i][j] = -1.0 / 6;
}
g[i][101] = 1;
}
}
g[100][100] = 1;
}
void gj(int n) {
for(int i = 1; i <= n; i++) {
int r = i;
for(int j = i + 1; j <= n; j++) {
if(fabs(g[r][i]) < fabs(g[j][i])) {
r = j;
}
}
if(r != i) {
for(int j = 1; j <= n + 1; j++) {
swap(g[i][j], g[r][j]);
}
}
for(int j = 1; j <= n; j++) {
if(j != i) {
double t = g[j][i] / g[i][i];
for(int k = i + 1; k <= n + 1; k++) {
g[j][k] -= g[i][k] * t;
}
}
}
}
for(int i = 1; i <= n; i++) {
x[i] = g[i][n + 1] /= g[i][i];
}
}
int main() {
#ifndef ONLINE_JUDGE
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
int T;
scanf("%d", &T);
while(T--) {
memset(to, -1, sizeof to);
memset(g, 0, sizeof g);
scanf("%d", &n);
for (int i = 0, u, v; i < n; ++i) {
scanf("%d%d", &u, &v);
to[u] = v;
}
build();
gj(100);
printf("Case %d: %.10lf\n", ++cs, x[1]);
}
}