「HNOI2013」游走

题目链接

问题分析

发现边经过的次数实际上就是点经过的次数乘上概率。那么问题就变成了求每个点经过的次数。

把无向边拆成两条有向边,然后把点 \(n\) 的所有出边都删掉。然后高斯消元即可。每个点经过的次数就是可以走到它的点的次数乘上概率之和。当然点 \(1\) 要额外加 \(1\) ,因为一开始是在点 \(1\)

参考程序

#include <cstdio>
#include <algorithm>
#include <cmath>

#define Maxn 510
#define Eps 1e-12
int n, m, Map[Maxn][Maxn], Sz[Maxn], Id[Maxn * Maxn];
double A[Maxn][Maxn], Times[Maxn * Maxn];

inline int Cmp(double x, double y) {
	if (fabs(x - y) < Eps) return 0;
	if (x - y > Eps) return -1; else return 1;
}

inline void Gauss() {
	for (int i = 1; i <= n; ++i) {
		for (int j = i; j <= n; ++j)
			if (Cmp(A[i][j], 0) != 0) {
				for (int k = 1; k <= n + 1; ++k)
					std::swap(A[i][k], A[j][k]);
				break;
			}
		double Tmp = A[i][i];
		for (int j = 1; j <= n + 1; ++j) A[i][j] /= Tmp;
		for (int j = 1; j <= n; ++j) {
			if (i == j) continue;
			Tmp = A[j][i];
			for (int k = 1; k <= n + 1; ++k)
				A[j][k] -= Tmp * A[i][k];
		}
	}
	return;
}

int main() {
	scanf("%d%d", &n, &m);
	for (int i = 1, x, y; i <= m; ++i) {
		scanf("%d%d", &x, &y);
		++Sz[x]; Map[x][y] = i;
		++Sz[y]; Map[y][x] = i;
	}
	for (int i = 1; i <= n; ++i) 
		for (int j = 1; j < n; ++j)
			if (Map[j][i])
				A[i][j] = -1.0 / Sz[j];
	for (int i = 1; i <= n; ++i) A[i][i] = 1;
	A[1][n + 1] = 1;
	Gauss();
	for (int i = 1; i <= n; ++i)
		for (int j = 1; j <= n; ++j)
			Times[Map[i][j]] = ((i != n) ? A[i][n + 1] / Sz[i] : 0) + ((j != n) ? A[j][n + 1] / Sz[j] : 0);
	for (int i = 1; i <= m; ++i) Id[i] = i;
	std::sort(Id + 1, Id + m + 1, [](int x, int y)->bool{ return Cmp(Times[x], Times[y]) == -1;});
	double Ans = 0;
	for (int i = 1; i <= m; ++i) Ans += Times[Id[i]] * i;
	printf("%.3lf\n", Ans);
	return 0;
}
posted @ 2019-12-15 21:45  chy_2003  阅读(150)  评论(0编辑  收藏  举报