【BZOJ3143】[Hnoi2013]游走
一个无向连通图,顶点从1编号到N,边从1编号到M。
小Z在该图上进行随机游走,初始时小Z在1号顶点,每一步小Z以相等的概率随机选 择当前顶点的某条边,沿着这条边走到下一个顶点,获得等于这条边的编号的分数。当小Z 到达N号顶点时游走结束,总分为所有获得的分数之和。
现在,请你对这M条边进行编号,使得小Z获得的总分的期望值最小。
期望次数大的标号应该小
p[e] = p[e.x] / out[e.x] + p[e.y] / out[e.y]
剩下的和3270相同
#include <iostream> #include <cmath> #include <cstdio> #include <algorithm> using namespace std; const int N = 510; int n, m, out[N]; double p[N][N]; bool map[N][N]; const double eps = 1e-9; struct Q { int x, y; double w; }e[N * N]; bool cmp(const Q& a, const Q& b) { return a.w > b.w; } void gauss(int n, double a[N][N]) { int i, j, k, r; for (i = 0; i < n; i ++) { r = i; for (j = i + 1; j < n; j ++) if (fabs(a[j][i]) > fabs(a[r][i])) r = j; if (r != i) for (j = 0; j <= n; j ++) swap(a[r][j], a[i][j]); for (k = i + 1; k < n; k ++) { long double f = a[k][i] / a[i][i]; for (j = i; j <= n; j ++) a[k][j] -= f * a[i][j]; } } for (i = n - 1; i >= 0; i --) { for (j = i + 1; j < n; j ++) a[i][n] -= a[j][n] * a[i][j]; a[i][n] /= a[i][i]; } } int main() { freopen("a.in", "r", stdin); scanf("%d%d", &n, &m); for (int i = 1, a, b; i <= m; i ++) { scanf("%d%d", &e[i].x, &e[i].y); e[i].x--; e[i].y--; map[e[i].x][e[i].y] = map[e[i].y][e[i].x] = true; out[e[i].x]++; out[e[i].y]++; } n --; for (int i = 0; i < n; i ++) for (int j = 0; j < n; j ++) if (map[i][j]) p[i][j] = 1.0 / out[j]; for (int i = 0; i < n; i ++) p[i][i] -= 1; p[0][n] = -1; gauss(n, p); for (int i = 1; i <= m; i ++) e[i].w = p[e[i].x][n] / out[e[i].x] + p[e[i].y][n] / out[e[i].y]; sort(e + 1, e + 1 + m, cmp); double ans = 0; for (int i = 1; i <= m; i ++) ans += e[i].w * i; printf("%.3lf\n", ans); return 0; }