题意:"蛇还有梯子"是一个在孟加拉国非常寻常的游戏。这个游戏简单到随便找到一个人都可以玩这个游戏。这个游戏的规则如下:
1.这里有一个100 * 100的棋盘,每个棋盘的数字从1到100.
2.你初始的位置在1.
3.每时你会投出一个骰子,这个骰子包含1~6的数字.
4.棋盘上有一些蛇和一些梯子。梯子会带你从一端到一段,蛇也一样。
5.如果你在梯子的末端,你会到达梯子的开端。如果你在蛇的开端,那么你也同样会到达蛇的末端。
6.如果你在第100个格子,那么游戏就会结束。
现在你需要求出期望的次数(抛出的骰子次数)从而赢得这场游戏。
分析:设\(E[i]\)为从点i投出骰子的期望次数,因此\(E[i] = \frac{1}{6}*(E[i + 1] + E[i + 2] + E[i + 3] +\dots+ E[i + 6] + 6)\),常数6表示如果从E[i]投出骰子,只要一次就可以达到\(E[i + 1]\)这些事件,先不考虑边界,当骰子投出100的时候,需要另外考虑,其它是会跳转的\(E[i] = E[ne[i]]\),如果这里有个梯子或者蛇。然后考虑边界的情况,即
\(E[i] = \frac{1}{6}*\sum\limits_{j = 1}^{k}E[i + 1] + \frac{1}{6}*\sum\limits_{j = k + 1}^{6}E[i] + 1\),规则里指明如果超出100的边界,会回到自己的位置。
然后我们就可以得到如下的方程式
\(1.6*E[i] - E[i + 1] - E[i + 2] -\dots-E[i + 6] = 6\)
\(2.k * E[i] - \sum\limits_{j = 1}^{k}E[i + j] = 6\)
\(3.E[i] - E[ne[i]] = 0\)
我们大致写一下这个方程式,可以用高斯消元解这个方程组,因为这个DP具有后效性。
这个方程有100行,因此常数6放在g[i][101]这个位置,其它位置放系数,然后用高斯消元解这个方程就可以了。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <cmath>
#include <algorithm>
using namespace std;
const int N = 110;
const int m = 100;
const double eps = 1e-8;
int ne[N];
//增广矩阵
double g[N][N];
int n;
int gauss()
{
int c, r;
for (c = 1, r = 1; c < m + 1; ++c)
{
int t = r;
for (int i = r; i < m + 1; ++i)
{
if (fabs(g[i][c]) > fabs(g[t][c]))
t = i;
if (fabs(g[t][c] < eps)) continue;
for (int i = c; i <= m + 1; ++i) swap(g[t][i], g[r][i]);
for (int i = m + 1; i >= c; --i) g[r][i] /= g[r][c];
for (int i = r + 1; i <= m; ++i)
{
if (fabs(g[i][c]) > eps)
{
for (int j = m + 1; j >= c; --j)
{
g[i][j] -= g[r][j] * g[i][c];
}
}
}
}
++r;
}
if (r < m + 1)
{
for (int i = r; i < m + 1; ++i)
{
if (fabs(g[i][m + 1]) > eps)
return 2;
}
return 1;
}
for (int i = m; i >= 1; --i)
{
for (int j = i + 1; j < m + 1; ++j)
g[i][m + 1] -= g[i][j] * g[j][m + 1];
}
return 0;
}
int main()
{
int t;
scanf("%d", &t);
int c = 0;
while (t--)
{
memset(ne, 0, sizeof ne);
scanf("%d", &n);
int a, b;
for (int i = 1; i <= n; ++i)
{
scanf("%d%d", &a, &b);
ne[a] = b;
}
printf("Case %d: ", ++c);
memset(g, 0, sizeof g);
for (int i = 1; i < 100; ++i)
{
if (ne[i])
{
//常数项
g[i][101] = 0;
//e[i] - e[ne[i]] = 0
g[i][i] = 1, g[i][ne[i]] = -1;
}
else
{
int cnt = 0;
for (int j = 1; i + j <= 100 && j <= 6; ++j)
{
++cnt;
g[i][i + j] = -1;
}
//系数,常数项
g[i][i] = cnt, g[i][101] = 6;
}
}
//1 * g[100][100] = 0
g[100][100] = 1, g[100][101] = 0;
gauss();
printf("%.12lf\n", g[1][101]);
}
return 0;
}