• BZOJ 3143 游走 | 数学期望 高斯消元


    啊 我永远喜欢期望题

    BZOJ 3143 游走

    题意

    有一个n个点m条边的无向联通图,每条边按1~m编号,从1号点出发,每次随机选择与当前点相连的一条边,走到这条边的另一个端点,一旦走到n号节点就停下。每经过一条边,要付出这条边的编号这么多的代价。现将所有边用1~m重新编号,使总代价的期望最小,求这个最小值。

    题解

    我们可以求出每条边的期望经过次数,然后贪心地让经过次数多的边编号小即可。

    直接用边来列方程求经过次数似乎列不出来,我们借助点来列方程。

    设x[u]为从某个点出发的次数的期望,v为与u相连的点,d[v]为点d的度,则:

    [x[u] = sum frac{x[v]}{d[v]} ]

    特殊地,不能从点n出发,所以x[n] = 0;第一次从点1出发,(x[u] = 1 + sum frac{x[v]}{d[v]})

    解出所有x后,设一条边的两个端点是u和v,则经过每条边的次数的期望是:

    [frac{x[u]}{d[u]} + frac{x[v]}{d[v]} ]

    代码如下:

    #include <cstdio>
    #include <cmath>
    #include <cstring>
    #include <algorithm>
    #define space putchar(' ')
    #define enter putchar('
    ')
    using namespace std;
    typedef long long ll;
    template <class T>
    void read(T &x){
        char c;
        bool op = 0;
        while(c = getchar(), c < '0' || c > '9')
    	if(c == '-') op = 1;
        x = c - '0';
        while(c = getchar(), c >= '0' && c <= '9')
    	x = x * 10 + c - '0';
        if(op) x = -x;
    }
    template <class T>
    void write(T x){
        if(x < 0) putchar('-'), x = -x;
        if(x >= 10) write(x / 10);
        putchar('0' + x % 10);
    }
    
    const int N = 505, M = 250005;
    int n, m, u[M], v[M], d[N];
    double ans[M], x[N], f[N][N], res;
    void build(){
        for(int i = 1; i <= n; i++)
    	f[i][i] = -1;
        for(int e = 1; e <= m; e++){
    	f[u[e]][v[e]] += 1.0 / d[v[e]];
    	f[v[e]][u[e]] += 1.0 / d[u[e]];
        }
        for(int i = 1; i <= n; i++)
    	f[n][i] = 0;
        f[n][n] = 1, f[n][n + 1] = 0;
        f[1][n + 1] = -1;
    }
    void Gauss(){
        for(int i = 1; i <= n; i++){
    	int l = i;
    	for(int j = i + 1; j <= n; j++)
    	    if(fabs(f[j][i]) > fabs(f[l][i])) l = j;
    	if(l != i)
    	    for(int j = i; j <= n + 1; j++)
    		swap(f[i][j], f[l][j]);
    	for(int j = n + 1; j >= i; j--)
    	    f[i][j] /= f[i][i];
    	for(int j = i + 1; j <= n; j++)
    	    for(int k = n + 1; k >= i; k--)
    		f[j][k] -= f[j][i] * f[i][k];
        }
        for(int i = n; i; i--){
    	x[i] = f[i][n + 1];
    	for(int j = 1; j < i; j++)
    	    f[j][n + 1] -= f[j][i] * x[i];
        }
    }
    int main(){
        read(n), read(m);
        for(int i = 1; i <= m; i++)
    	read(u[i]), read(v[i]), d[u[i]]++, d[v[i]]++;
        build();
        Gauss();
        for(int i = 1; i <= m; i++)
    	ans[i] = x[u[i]] / d[u[i]] + x[v[i]] / d[v[i]];
        sort(ans + 1, ans + m + 1);
        for(int i = 1; i <= m; i++)
    	res += ans[i] * (m - i + 1);
        printf("%.3lf
    ", res);
        return 0;
    }
    
  • 相关阅读:
    云计算
    uc/xi
    互联网 2.0概念
    java连接数据库
    记录一下安装 mysql 的踩坑之路
    1
    Linux介绍与基本必知命令
    Web课程Linux命令统计
    7——条件判断、三目运算、条件循环、迭代循环
    6——散列类型、运算符优先级、逻辑运算
  • 原文地址:https://www.cnblogs.com/RabbitHu/p/BZOJ3143.html
Copyright © 2020-2023  润新知