bzoj3143

【bzoj3143】[Hnoi2013]游走

题目描述

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

输入

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

输出

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

样例输入

3 3 
2 3
1 2
1 3

样例输出

3.333

 

sol:十分显然的是要让经过概率高的边权尽可能小,难点就在于求每条边被经过的概率

令一个点 x 的度数为 Degx,那么对于一条边<U,V>,被经过的概率就是(经过点U的概率*1/Deg[U]+经过点V的概率*1/Deg[V])

经过某个点的概率就是∑与它相连的点的概率/那个点的度数,这个显然是一个n个未知数,n个式子的一次方程组,可以用高斯消元求解

Ps:经过1的概率要+1,经过n的概率是0,因为到了n是不会出来的

// luogu-judger-enable-o2
#include <bits/stdc++.h>
using namespace std;
typedef int ll;
inline ll read()
{
    ll s=0;
    bool f=0;
    char ch=' ';
    while(!isdigit(ch))
    {
        f|=(ch=='-'); ch=getchar();
    }
    while(isdigit(ch))
    {
        s=(s<<3)+(s<<1)+(ch^48); ch=getchar();
    }
    return (f)?(-s):(s);
}
#define R(x) x=read()
inline void write(ll x)
{
    if(x<0)
    {
        putchar('-'); x=-x;
    }
    if(x<10)
    {
        putchar(x+'0');    return;
    }
    write(x/10);
    putchar((x%10)+'0');
    return;
}
#define W(x) write(x),putchar(' ')
#define Wl(x) write(x),putchar('\n')
const int N=505,M=500005;
int n,m;
int tot=0,Next[M],to[M],head[N],Deg[N];
double a[N][N],b[N];
struct Bian
{
    int U,V;
    double Gl;
}E[M];
inline bool cmp_Gl(Bian p,Bian q)
{
    return p.Gl<q.Gl;
}
inline void add(int x,int y)
{
    Deg[x]++;
    Next[++tot]=head[x];
    to[tot]=y;
    head[x]=tot;
}
inline void Debug(int n)
{
    int i,j;
    for(i=1;i<=n;i++)
    {
        for(j=1;j<=n;j++) printf("%.2lf ",a[i][j]);
        printf("%.2lf",b[i]);
        putchar('\n');
    }
    putchar('\n');
}
inline void Gauss(int n)
{
    int i,j,k;
    double Div;
    for(i=1;i<=n;i++)
    {
        int Pos=i;
        for(j=i+1;j<=n;j++) if(fabs(a[Pos][i])<fabs(a[j][i])) Pos=j;
        if(Pos!=i)
        {
            swap(a[Pos],a[i]); swap(b[Pos],b[i]);
        }
        Div=a[i][i];
        for(j=i;j<=n;j++) a[i][j]/=Div; b[i]/=Div;
        for(j=1;j<=n;j++) if(j!=i)
        {
            double Div=a[j][i];
            for(k=i;k<=n;k++) a[j][k]-=Div*a[i][k];
            b[j]-=Div*b[i];
        }
//        Debug(n);
    }
}
int main()
{
    int i,j;
    R(n); R(m);
    for(i=1;i<=m;i++)
    {
        int x=read(),y=read();
        add(x,y); add(y,x);
        E[i].U=x; E[i].V=y;
    }
    for(i=1;i<n;i++)
    {
        a[i][i]=1.0;
        for(j=head[i];j;j=Next[j]) if(to[j]!=n)
        {
            a[i][to[j]]=-1.0/Deg[to[j]];
        }
        b[i]=(i==1)?1:0;
    }
    Gauss(n-1);
    for(i=1;i<=m;i++)
    {
        E[i].Gl=1.0*b[E[i].U]/Deg[E[i].U]+1.0*b[E[i].V]/Deg[E[i].V];
    }
    sort(E+1,E+m+1,cmp_Gl);
    double ans=0;
    for(i=1;i<=m;i++)
    {
        ans+=1.00*E[i].Gl*(m-i+1);
    }
    printf("%.3lf\n",ans);
    return 0;
}
/*
input
3 3
2 3
1 2
1 3
output
3.333
*/
View Code

 

posted @ 2019-04-15 22:11  yccdu  阅读(197)  评论(0编辑  收藏  举报