【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;
}

 

posted @ 2015-06-16 14:40  Moretimes  阅读(1419)  评论(0编辑  收藏  举报