BZOJ 3143: [Hnoi2013]游走 概率与期望+高斯消元

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位小数。

给定一个简单无向连通图,求给每个边分配一个边权,使得从 $1$ 号点到 $n$ 号点经过边权和的期望值最小. (分配的边权互不相同,且值域为 $[1,m]$).
首先有一个结论,$1$ 到 $n$ 号点权和的期望值为 $sum_{i=1}^{m}val[i] imes perc[i],$即每条边的边权乘以走这条边的概率.
然后,我们发现经过每条边的概率只与这条边两个端点有关,即分别走到两个端点的概率以及两个端点的度数.
具体地,我们可以列出这样的方程组:($f[i]$ 表示从起点走到 $i$ 的概率)
$f[i]=sum f[from] imes deg[from].$
如果这是一个 $DAG,$ 则可以直接来一遍裸的 dp,然而由于有环的存在,只能用高斯消元.
即将式子变为 $f[i]-sum f[from] imes deg[from]=0.$
可以将这个看作是关于 $i$ 的一个方程组,列出方程矩阵之后解一下即可.
特别的,由于肯定会经过 $1$ 号点,所以高斯消元中 $1$ 所对应的那个方程组的常数项为 $1.$

其实呢,你也可以把这个概率理解为期望经过该点的次数. 

而你发现在这个情况下这个期望出现的次数和概率是等价的.  

#include <cmath>         
#include <cstdio> 
#include <algorithm>  
#define N 503 
#define M 205089
#define setIO(s) freopen(s".in","r",stdin) 
using namespace std;   
double f[N][N],G[M];       
int edges; 
int hd[N],to[M<<1],nex[M<<1],deg[N],U[M],V[M];    
void add(int u,int v) 
{
    nex[++edges]=hd[u],hd[u]=edges,to[edges]=v; 
}
void Gauss(int n) 
{ 
    int i,j,k,now; 
    for(i=1;i<=n;++i) 
    {
        now=i;          
        for(j=i;j<=n;++j) 
        {
            if(fabs(f[j][i])>fabs(f[now][i])) now=j;   
        } 
        if(now!=i) 
        {
            for(j=1;j<=n;++j) swap(f[i][j],f[now][j]); 
        } 
        if(f[i][i]) 
        { 
            for(j=i+1;j<=n+1;++j) f[i][j]/=f[i][i]; 
            f[i][i]=1; 
        }
        for(j=i+1;j<=n;++j) 
        {
            double div=f[j][i];    
            for(k=i+1;k<=n+1;++k) f[j][k]-=div*f[i][k];                                          
            f[j][i]=0; 
        }  
    } 
    for(i=n;i>=1;--i) 
    {
        for(j=i+1;j<=n;++j) 
        {
            f[i][n+1]-=f[j][n+1]*f[i][j];    
        }
    }
}                    
int main() 
{ 
    int i,j,n,m; 
    // setIO("input");     
    scanf("%d%d",&n,&m);    
    for(i=1;i<=m;++i) 
    {
        int u,v; 
        scanf("%d%d",&u,&v),add(u,v),add(v,u),++deg[u],++deg[v],U[i]=u,V[i]=v;  
    }  
    f[1][n]=1.0; 
    for(i=1;i<n;++i) 
    {
        f[i][i]=1; 
        for(j=hd[i];j;j=nex[j]) 
        {
            int v=to[j]; 
            if(v==n) continue;    
            f[i][v]=-1.0/deg[v];        
        }
    } 
    Gauss(n-1);    
    for(i=1;i<=m;++i) 
    {
        int a=U[i],b=V[i];
        if(a!=n) 
        { 
            G[i]+=f[a][n]*(1.0/deg[a]);    
        } 
        if(b!=n) 
        { 
            G[i]+=f[b][n]*(1.0/deg[b]);  
        }
    } 
    sort(G+1,G+1+m);   
    double ans=0; 
    for(i=1;i<=m;++i) 
    {
        ans+=G[i]*((m-i+1)*1.0); 
    } 
    printf("%.3lf
",ans); 
    return 0;    
}

  

原文地址:https://www.cnblogs.com/guangheli/p/11528077.html