题意:"蛇还有梯子"是一个在孟加拉国非常寻常的游戏。这个游戏简单到随便找到一个人都可以玩这个游戏。这个游戏的规则如下:
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具有后效性。

\[6e[1] - e[2] - e[3] - ... - e[6] = 6\\ \quad\quad\quad\quad\quad 6e[2] - e[3] - ... - e[6] - e[7] = 6\\ \quad\quad\quad\quad\quad\quad\quad\quad\quad\quad 6e[3] - ... - e[6] - e[7] - e[8] = 6\\ \dots \]

这个方程有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;
}