• BZOJ3143:[HNOI2013]游走——题解


    http://www.lydsy.com/JudgeOnline/problem.php?id=3143

    Description

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

    Input

    第一行是正整数N和M,分别表示该图的顶点数 和边数,接下来M行每行是整数u,v(1≤u,v≤N),表示顶点u与顶点v之间存在一条边。 输入保证30%的数据满足N≤10,100%的数据满足2≤N≤500且是一个无向简单连通图。

    Output

    仅包含一个实数,表示最小的期望值,保留3位小数。

    Sample Input

    3 3
    2 3
    1 2
    1 3

    Sample Output

    3.333

    参考http://blog.csdn.net/vmurder/article/details/44542575

    期望不会啊怎么办……

    首先我们很容易想到一个贪心:将所有的边走过的期望求出,大期望配小编号即可。

    那么求边的期望w,它的左右端点为u和v,度分别为d[u],d[v],走过点的期望为x[u],x[v],那么显然:

    w=x[u]/d[u]+x[v]/d[v]。

    现在变成了求x,显然就是与它相邻的点的(期望/度数)之和。

    但是x1显然需要在原有的基础上+1(因为从该点出发必然经过一次)

    xn显然是0(虽然是有且仅有一次会进去,所以理论上应当为1,但是一旦进去就出不来了,对于所有其他的运算来说xn即为0,那我们更新的时候直接把他当做0走即可)

    剩下的就是高斯消元了。

    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<cmath>
    using namespace std;
    typedef double dl;
    const int N=505;
    const int M=N*N;
    int u[M],v[M],d[N];
    dl f[N][N],x[N],w[M];
    inline void Gauss(int n,int m){
        for(int i=1;i<=n;i++){
            int l=i;
            for(int j=l+1;j<=n;j++)
                if(fabs(f[l][i])<fabs(f[j][i]))l=j;
            if(l!=i)
                for(int j=i;j<=m;j++)
                    swap(f[l][j],f[i][j]);
            for(int j=i+1;j<=n;j++){
                dl temp=f[j][i]/f[i][i];
                for(int k=i;k<=m;k++)
                    f[j][k]=f[j][k]-f[i][k]*temp;
            }
        }
        for(int i=n;i>=1;i--){
            dl t=f[i][m];
            for(int j=n;j>i;j--)
                t-=x[j]*f[i][j];
            x[i]=t/f[i][i];
        }
        return ;
    }
    int main(){
        int n,m;
        scanf("%d%d",&n,&m);
        for(int i=1;i<=m;i++){
            scanf("%d%d",&u[i],&v[i]);
            d[u[i]]++;d[v[i]]++;
        }
        for(int i=1;i<n;i++)f[i][i]=-1;
        for(int i=1;i<=m;i++){
            f[u[i]][v[i]]+=1.0/d[v[i]];
            f[v[i]][u[i]]+=1.0/d[u[i]];
        }
        for(int i=1;i<=n;i++)f[n][i]=0;
        f[1][n+1]=-1,f[n][n]=1;
        Gauss(n,n+1);
        for(int i=1;i<=m;i++)w[i]=x[u[i]]/d[u[i]]+x[v[i]]/d[v[i]];
        sort(w+1,w+m+1);
        dl ans=0;
        for(int i=1;i<=m;i++)ans+=(m-i+1)*w[i];
        printf("%.3f
    ",ans);
        return 0;
    }
  • 相关阅读:
    结构体位域与规范定义顺序的问题
    visual studio 2015使用MFC的console调试打印
    MFC笔记
    MFC中解决文本保存到文件时乱码问题
    C/C++关于文件的读写操作以及文件的打开和保存
    MFC使用自定义消息
    MFC输入框CEdit控件十六进制转换
    Visual studio C++ MFC应用程序自动探测串口号
    visual C++ MFC串口编程overlapped结构汇总
    模块及模块间的接口方式
  • 原文地址:https://www.cnblogs.com/luyouqi233/p/8271205.html
Copyright © 2020-2023  润新知