【BZOJ-3143】游走 高斯消元 + 概率期望

3143: [Hnoi2013]游走

Time Limit: 10 Sec  Memory Limit: 128 MB
Submit: 2264  Solved: 987
[Submit][Status][Discuss]

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

HINT

边(1,2)编号为1,边(1,3)编号2,边(2,3)编号为3。

Source

非官方数据

Solution

和 博物馆 那道题类似,列期望方程高斯消元得解。

如果对边进行处理,复杂度是$O((N^{2})^{3})$的,所以考虑利用点来求解边。

设未知数$X_{i}$表示第$i$号点的期望经过次数。那么显然有$X_{i}=\sum X_{j}$这样就可以列方程了。

显然有两个特例,必然从$1$号点出发,所以$X_{1}-1=\sum X_{j}$,以及必然从$N$号点结束,所以$X_{N}=0$,其余的可以得解。

对于一条边$<u,v>$,经过这条边的期望次数就是$\frac {X_{u}} {d[u]}+\frac {X_{v}}{d[v]}$,总期望最小,就是期望次数越小的边标号越大即可。

Code

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
inline int read()
{
    int x=0,f=1; char ch=getchar();
    while (ch<'0' || ch>'9') {if (ch=='-') f=-1; ch=getchar();}
    while (ch>='0' && ch<='9') {x=x*10+ch-'0'; ch=getchar();}
    return x*f;
}
#define MAXN 550
#define eps 1e-5
int N,M,mp[MAXN][MAXN],flag,d[MAXN];
double a[MAXN][MAXN],X[MAXN],p[MAXN*MAXN],ans;
inline void Debug()
{
    puts("=========");
    for (int i=1; i<=N; i++,puts(""))
        for (int j=1; j<=N+1; j++) printf("%.2lf ",a[i][j]);
    puts("");
}
inline void Gauss()
{
    flag=1;
    for (int i=1; i<=N; i++)
        {
            int mx=i;
            for (int j=i+1; j<=N; j++)
                if (abs(a[j][i])>abs(a[mx][i])) mx=j;
            swap(a[i],a[mx]);
            if (abs(a[i][i])<eps) {flag=-1; continue;}
            for (int j=i+1; j<=N+1; j++) if (abs(a[i][j])>0) a[i][j]/=a[i][i];
            a[i][i]=1.0;
                     
            for (int j=i+1; j<=N; j++)
                {
                    for (int k=i+1; k<=N+1; k++) 
                        a[j][k]-=a[j][i]*a[i][k];
                    a[j][i]=0.0;
                }
//            Debug();
        }
    for (int i=1,f=1; i<=N; i++,f=1)
        {
            for (int j=1; j<=N && f; j++)
                if (abs(a[i][j])>eps) f=0;
            if (abs(a[i][M+1])>eps && f) flag=0;
        }
    if (flag==0) return;
    for (int i=N; i>=1; i--)
        {
            X[i]=a[i][N+1];
            for (int j=i+1; j<=N; j++) X[i]-=X[j]*a[i][j];
        }
}
int main()
{
    N=read(),M=read();
    for (int i=1,x,y; i<=M; i++) x=read(),y=read(),mp[x][y]=mp[y][x]=1,d[x]++,d[y]++; 
    for (int i=1; i<=N-1; i++)
        for (int j=1; j<=N-1; j++)
            if (mp[i][j]) a[i][j]=-1.0/d[j];
    a[1][N+1]=1.0;
    for (int i=1; i<=N; i++) a[i][i]=1.0;
//  Debug();
    Gauss();
    for (int i=1,tot=0; i<=N; i++)
        for (int j=i+1; j<=N; j++)
            if (mp[i][j]) p[++tot]=X[i]/d[i]+X[j]/d[j];
    sort(p+1,p+M+1);
//  for (int i=1; i<=M; i++) printf("%.2lf  ",p[i]); puts("");
    for (int i=1; i<=M; i++) ans+=p[i]*(M-i+1);
    printf("%.3lf\n",ans);
    return 0;
}

 

posted @ 2016-12-05 11:22  DaD3zZ  阅读(284)  评论(0编辑  收藏  举报