zoukankan      html  css  js  c++  java
  • P3232 [HNOI2013]游走——无向连通图&&高斯消元

    题意

    一个无向连通图,顶点从1编号到N,边从1编号到M。 小Z在该图上进行随机游走,初始时小Z在1号顶点,每一步小Z以相等的概率随机选 择当前顶点的某条边,沿着这条边走到下一个顶点,获得等于这条边的编号的分数。当小Z 到达N号顶点时游走结束,总分为所有获得的分数之和。 现在,请你对这M条边进行编号,使得小Z获得的总分的期望值最小。(2<=N<=500)

    分析

    直接算边的期望会很大,考虑先算点的期望。

    设 $E(i)$ 为经过第 $i$ 个点的期望次数,$D(i)$ 为 $i$ 的度数,设 $v$ 为与 $u$ 相连的点,则

    $$E(u) = sum_{v, v eq n} frac{E(v)}{D(v)}$$

    从而,经过一条边 $(u, v)$ 的期望次数为

    $$E(u, v)= frac{E(u)}{D(u)} + frac{E(v)}{D(v)}$$

    有两点需要注意,结点1期望步数需要加1(可假设有结点0,以概率1转移到结点1);由于到结点 $n$ 就结束了,不要考虑来自 $n$ 的步数。

    由于是无向连通图,不用考虑0和无穷大,方程组有唯一的解。

    既然求出每条边的期望次数,当然是给次数大的分配小编号,次数小的分配大编号。

    #include<bits/stdc++.h>
    using namespace std;
    
    const int maxn = 500+10;
    typedef double Matrix[maxn][maxn];
    
    //要求系数矩阵可逆
    //这里的A是增广矩阵,即A[i][n] 是第i个方程右边的常数bi
    //运行结束后A[i][n] 是第i个未知数的值
    void gauss_elimination(Matrix A, int n)
    {
        int i, j, k, r;
    
        for(i = 0;i < n;i++)  //消元过程
        {
            //选绝对值一行r并与第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]);
    
            //与第i+1~n行进行消元
            for(k = i+1; k < n;k++)
            {
                double f = A[k][i] / A[i][i];
                for(int j = i;j <= n;j++)  A[k][j] -= f * A[i][j];      //已经是阶梯型矩阵了,所以从i开始
            }
        }
    
        //回代过程
        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];
        }
    }
    
    void debug_print(Matrix A, int n)
    {
        for(int i = 0;i < n;i++)
            for(int j = 0;j <= n;j++)  printf("%f%c", A[i][j], j == n ? '
    ' : ' ');
    }
    
    int n, m;
    vector<int>edges[maxn];
    int d[maxn];
    Matrix  A;
    vector<double>e;
    
    int main()
    {
        scanf("%d%d", &n, &m);
        for(int i = 0;i < m;i++)
        {
            int u, v;
            scanf("%d%d", &u, &v);
            u--; v--;   //改成从0开始编号
            edges[u].push_back(v);
            edges[v].push_back(u);
            d[u]++; d[v]++;
        }
    
        //构造方程组
        for(int i = 0;i <n;i++)
        {
            A[i][i] = 1;
            for(int j = 0;j <edges[i].size();j++)
                if(edges[i][j] != n-1)  A[i][edges[i][j]] -= 1.0/d[edges[i][j]];
            if(i == 0)  A[i][n] = 1;
        }
    
        //debug_print(A, n);
    
        gauss_elimination(A, n);
    
        for(int i = 0;i < n;i++)
        {
            for(int j = 0;j < edges[i].size();j++)
            {
                double tmp = 0;
                if(i != n-1)  tmp += A[i][n]/d[i];
                if(edges[i][j] != n-1)  tmp +=  + A[edges[i][j]][n]/d[edges[i][j]];  //不是终点时
                e.push_back(tmp);
            }
        }
    
        sort(e.begin(), e.end());
    
    //    for(int i = 0;i < e.size();i++)  printf("%f ", e[i]);
    //    printf("
    ");
    
        double res = 0;
        for(int i = 0;i < m;i++)
        {
            res += e[2*i]*(m-i);  //无向边重复了一次,所以隔一个取一个
        }
        printf("%.3f
    ", res);
    }

    参考链接:https://www.luogu.org/problemnew/solution/P3232

  • 相关阅读:
    QSet<T>自定义类型需要定义==和qHash()函数
    《左耳听风》-ARTS-打卡记录-第十三周
    Windows中对窗口进行剪切
    Markdown 编写规范
    【洛谷 P1033】自由落体
    【GOJ 3032】司愁之路
    动态规划基础 3-解题报告
    前缀、中缀、后缀互相转换
    【GOJ 3015】疯狂外星人
    【GOJ 3010】有趣的数
  • 原文地址:https://www.cnblogs.com/lfri/p/11543107.html
Copyright © 2011-2022 走看看