【BZOJ】3143: [Hnoi2013]游走-高斯消元&期望DP

传送门:bzoj3143


题解

l i l_i li的期望经过次数为 w i w_i wi

贪心将边按 w w w降序排序,答案即 ∑ i ⋅ w i \sum i·w_i iwi

但边的期望不好统计,考虑转成点:
d p i dp_i dpi表示点 i i i的期望经过次数, d i d_i di为点的出度,则
w = [ u ≠ n ] d p u d u + [ v ≠ n ] d p v d v w=[u\neq n]\frac{dp_u}{d_{u}}+[v\neq n]\frac{dp_v}{d_v} w=[u̸=n]dudpu+[v̸=n]dvdpv

对于前 n − 1 n-1 n1个点,存在环形的DP转移,高斯消元求解即可。


代码

#include
using namespace std;
typedef double db;
const int N=550;
const db eps=1e-12;
int n,m,g[N][N],tot,to[N];
db a[N][N],p[N],ky[N*N],ans;
 
inline bool vl(db x)
{return (x<=eps) && (x>=-eps);}
 
inline void init()
{
    int i,j,ix,iy;
    scanf("%d%d",&n,&m);
    for(i=1;i<=m;++i){
        scanf("%d%d",&ix,&iy);
        to[ix]++;to[iy]++;
        g[ix][iy]=g[iy][ix]=1;
    }   
}
 
inline void build()
{
    int i,j;
    a[1][n]=1.0;
    for(i=1;i<n;++i){
        a[i][i]=1.0;
        for(j=1;j<n;++j) if(g[i][j])
           a[i][j]=-1.0/(db)to[j];  
    }
}
 
inline void gauss()
{
    int i,j,k,nw;db bs;
    for(i=1;i<n;++i){
        if(vl(a[i][i])){
            for(j=i+1;j<n;++j)
             if(!vl(a[j][i])){nw=j;break;}
            for(j=i;j<=n;++j) 
            swap(a[nw][j],a[i][j]);
        }
        for(j=i+1;j<n;++j) if(!vl(a[j][i])){
            bs=-a[j][i]/a[i][i];
            for(k=i;k<=n;++k)
             a[j][k]+=bs*a[i][k];
        }
    }
}
 
inline void get()
{
    int i,j;
    for(i=n-1;i;--i){
        p[i]=a[i][n]/a[i][i];
        for(j=i-1;j;--j)
            a[j][n]-=a[j][i]*p[i];
    }
    for(i=1;i<n;++i)
     for(j=i+1;j<=n;++j) if(g[i][j]){
        tot++;ky[tot]=p[i]/(db)to[i]+p[j]/(db)to[j];
     }
    sort(ky+1,ky+tot+1);
    for(i=1;i<=m;++i) ans+=ky[i]*(m-i+1.0); 
    printf("%.3lf\n",ans);
}
 
int main(){
    init();
    build();
    gauss();
    get();
}

你可能感兴趣的:(高斯消元,---线性代数---,概率与期望)